Core Java

@MapsId Annotation Example

1. Introduction

The @MapsId annotation provided by JPA 2.0 is used to map a foreign key in a one-to-one or many-to-one relationship to the primary key of another entity. In this example, I will create a spring-boot project that utilizes @MapsId annotation along with both @ManyToOne and @OneToOne annotations for the following four tables:

  • T_ORDER: the primary key is the ID column.
  • T_PRODUCT: the primary key is the ID column.
  • T_ORDER_ITEM: the primary key is the composite key of ORDER_ID and PRODUCT_ID. Both are foreign keys to the T_ORDER and T_PRODUCT tables with the many-to-one relationship.
  • T_PRODUCT_PROFILE: the primary key is the ID column, which is also the foreign key to the T_PRODUCT table with the one-to-one relationship.
Figure 1 MapsId Annotation ER Diagram
Figure 1. ER Diagram

2. Setup

In this step, I will create a gradle project along with JDK17, Spring Web, Spring Data JPA, Lombok, H2, and Junit5 libraries via Spring Initializer.

2.1 Gradle Build File

No modification is needed for the generated build.gradle file.

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.4.0'
	id 'io.spring.dependency-management' version '1.1.6'
}

group = 'org.zheng.demo'
version = '0.0.1-SNAPSHOT'

java {
	toolchain {
		languageVersion = JavaLanguageVersion.of(17)
	}
}

configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
	useJUnitPlatform()
}

2.2 Spring Boot Application

No modification is needed for the generated DemoApplication.java file.

DemoApplication

package org.zheng.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}

}

2.3 Spring Properties

Update the generated application.properties file to enable the formatted Hibernate logging for SQL statements and binding parameter values.

application.properties

spring.application.name=demo

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.orm.jdbc.bind=trace

spring.h2.console.enabled=true

3. One-to-One with @MapsId

3.1 Product Entity

In this step, I will create a Product.java entity class that has a name and id fields.

Product.java

package org.zheng.demo.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "T_PRODUCT")
@Data
@NoArgsConstructor
public class Product {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	private String name;

}

3.2 ProductProfile Entity with @MapsId Annotation

In this step, I will create a ProductProfile.java entity class that has a productDetail and id fields. The @MapsId("id") annotation maps the id field as the foreign key to the Product entity’s primary key with the @OneToOne annotation.

ProductProfile.java

package org.zheng.demo.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.MapsId;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "T_PRODUCT_PROFILE")
@Data
@NoArgsConstructor
public class ProductProfile {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@MapsId("id") // maps to primary "id"
	@OneToOne
	@JoinColumn(name = "id") // foreign key column in T_PRODUCT Table
	private Product product;
	
	private String productDetail;

}
  • Line 23: the "id" attribute in the @MapsId("id") annotation indicates that the primary key “id” is the foreign key of the Product entity.
  • Line 24: the @OneToOne annotation indicates that Product and ProductProfile have the One-to-One relationship.

3.3 Product Repository

In this step, I will create a ProductRepo.java interface that extends from JpaRepository.

ProductRepo.java

package org.zheng.demo.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.zheng.demo.entity.Product;

@Repository
public interface ProductRepo extends JpaRepository<Product, Integer> {
	
	List<Product> findByName(String prodName);
}

3.4 ProductProfile Repository

In this step, I will create a ProductProfileRepo.java interface that extends from JpaRepository.

ProductProfileRepo.java

package org.zheng.demo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.zheng.demo.entity.ProductProfile;

@Repository
public interface ProductProfileRepo extends JpaRepository<ProductProfile, Integer> {

}

3.5 Test Product Repository

In this step, I will create a ProductRepoTest.java class that tests the save and find methods.

ProductRepoTest.java

package org.zheng.demo.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.transaction.annotation.Transactional;
import org.zheng.demo.entity.Product;
import org.zheng.demo.entity.ProductProfile;

@DataJpaTest
@Transactional
class ProductRepoTest {

	@Autowired
	private ProductRepo prodRepo;

	@Autowired
	private ProductProfileRepo prodProfileRepo;

	@Test
	void save_product_and_profile() {
		Product prod = new Product();
		prod.setName("product");

		prodRepo.save(prod);

		ProductProfile prodProfile = new ProductProfile();
		prodProfile.setProduct(prod);
		prodProfile.setProductDetail("some details");

		prodProfileRepo.save(prodProfile);

		Optional<Product> foundProd = prodRepo.findById(prod.getId());
		Optional<ProductProfile> foundProdProfile = prodProfileRepo.findById(prodProfile.getId());

		assertTrue(foundProd.isPresent());
		assertTrue(foundProdProfile.isPresent());
		assertEquals(foundProd.get().getId(), foundProdProfile.get().getId());
	}

}

4. Many-to-One with @MapsId

4.1 Order Entity

In this step, I will create an Order.java entity class that has a name and id fields.

Order.java

package org.zheng.demo.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "T_ORDER")
@Data
@NoArgsConstructor
public class Order {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	private String name;
}

4.2 OrderItemId Embeddable

In this step, I will create an OrderItemId.java class annotated with @Embeddable to define a composite key with both orderId and productId fields.

OrderItemId.java

package org.zheng.demo.entity;

import java.io.Serializable;
import java.util.Objects;

import jakarta.persistence.Embeddable;
import lombok.Getter;
import lombok.Setter;

@Embeddable
@Getter
@Setter
public class OrderItemId implements Serializable {

	private static final long serialVersionUID = 2946564272714473110L;
	private Integer orderId;
	private Integer productId;

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		OrderItemId other = (OrderItemId) obj;
		return Objects.equals(orderId, other.orderId) && Objects.equals(productId, other.productId);
	}

	@Override
	public int hashCode() {
		return Objects.hash(orderId, productId);
	}
}

4.3 OrderLineItem Entity With @MapsId Annotation

In this step, I will create an OrderLineItem.java entity class that uses @MapsId annotation to map the orderId field as the foreign key to the Order entity’s primary key with the @ManyToOne annotation. It also maps the productId fields as the foreign key to the Product entity’s primary key with @MapsId.

OrderLineItem.java

package org.zheng.demo.entity;

import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapsId;
import jakarta.persistence.Table;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "T_ORDER_ITEM")
@Data
@NoArgsConstructor
public class OrderLineItem {

	@EmbeddedId
	private OrderItemId id;// primary key of the order line item

	@MapsId("orderId") // maps to the "orderId" field in "OrderItemId" class
	@ManyToOne
	@JoinColumn(name = "order_id") // foreign key column in T_Order_Item Table
	private Order order;

	@MapsId("productId") // maps to "productId" in "OrderItemId"
	@ManyToOne
	@JoinColumn(name = "product_id") // foreign key column in T_Order_Item Table
	private Product product;

	private Integer quantity;
}
  • Line 18: @EmbeddedId annotation for the @Embeddable composite key defined in step 4.2.
  • Line 21: @MapsId annotation maps the orderId field as the foreign key to the Order entity’s primary key with the @ManyToOne annotation.
  • Line 26: @MapsId annotation maps the productId field as the foreign key to the Product entity’s primary key with the @ManyToOne annotation.

4.4 Order Repository

In this step, I will create an OrderRepo.java interface that extends from JpaRepository.

OrderRepo.java

package org.zheng.demo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.zheng.demo.entity.Order;

@Repository
public interface OrderRepo extends JpaRepository<Order, Integer> {

}

4.5 OrderLineItem Repository

In this step, I will create an OrderLineItemRepo.java interface that extends from JpaRepository.

OrderLineItemRepo.java

package org.zheng.demo.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.zheng.demo.entity.OrderLineItem;

@Repository
public interface OrderLineItemRepo extends JpaRepository<OrderLineItem, Integer> {

}

4.6 Test Order Repository

In this step, I will create an OrderRepoTest.java to test both save and find methods.

OrderRepoTest.java

package org.zheng.demo.repository;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.annotation.Transactional;
import org.zheng.demo.entity.Order;
import org.zheng.demo.entity.OrderItemId;
import org.zheng.demo.entity.OrderLineItem;
import org.zheng.demo.entity.Product;
import org.zheng.demo.entity.ProductProfile;

@DataJpaTest
@Transactional
class OrderRepoTest {

	@Autowired
	private OrderLineItemRepo lineItemRepo;

	@Autowired
	private OrderRepo orderRep;

	@Autowired
	private ProductProfileRepo prodProfRepo;

	@Autowired
	private ProductRepo prodRepo;

	private Product prod1;
	private Product prod2;

	@BeforeEach
	void setup() {
		prod1 = saveProduct("product 1", "some details");
		prod2 = saveProduct("product 2", "some details");
	}

	private Order buildOrder(final String orderName) {
		Order order = new Order();
		order.setName(orderName);
		return order;
	}

	private OrderLineItem buildOrderItem(final Order order, final Product prod, final Integer itemQuantity) {
		OrderLineItem item = new OrderLineItem();

		OrderItemId itemId1 = new OrderItemId();
		itemId1.setOrderId(order.getId());

		itemId1.setProductId(prod.getId());

		item.setId(itemId1);
		item.setOrder(order);
		item.setProduct(prod);
		return item;
	}

	@Test
	void test_save_order() {
		// create an order
		Order order = buildOrder("first order");
		assertNull(order.getId());
		orderRep.save(order);
		assertNotNull(order.getId());

		// create an order line item
		OrderLineItem item1 = buildOrderItem(order, prod1, 1);
		lineItemRepo.save(item1);

		OrderLineItem item2 = buildOrderItem(order, prod2, 2);
		lineItemRepo.save(item2);

		List<OrderLineItem> items = lineItemRepo.findAll();
		assertEquals(2, items.size());
	}

	@Test
	void test_save_order_update() {
		// create an order
		Order order = buildOrder("first order");
		orderRep.save(order);

		// create an order line item
		OrderLineItem item1 = buildOrderItem(order, prod1, 1);
		lineItemRepo.save(item1);

		item1.setQuantity(2);

		lineItemRepo.save(item1);

		List<OrderLineItem> items = lineItemRepo.findAll();
		assertEquals(1, items.size());
	}

	@Test
	void test_save_item_wo_prod_throw_error() {
		// create an order
		Order order = buildOrder("first order");
		orderRep.save(order);

		// create an order line item
		OrderLineItem item1 = buildOrderItem(order, prod1, 1);
		item1.setProduct(null);
		JpaSystemException jpaException = assertThrows(JpaSystemException.class, () -> lineItemRepo.save(item1));
		assertTrue(jpaException.getMessage().contains(
				"attempted to assign id from null one-to-one property [org.zheng.demo.entity.OrderLineItem.product"));

	}

	private Product saveProduct(final String productName, final String prodDetail) {
		Product prod = new Product();
		prod.setName(productName);

		prod = prodRepo.save(prod);

		ProductProfile prodProfile = new ProductProfile();
		prodProfile.setProduct(prod);
		prodProfile.setProductDetail(prodDetail);

		prodProfRepo.save(prodProfile);

		return prod;
	}

}

5. Demonstrate Controller

5.1 Demo Service

In this step, I will create a DemoService.java service that wires the entities and repositories to save a product along with its profile data. It also saves an order and its line items.

DemoService.java

package org.zheng.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.zheng.demo.entity.Order;
import org.zheng.demo.entity.OrderItemId;
import org.zheng.demo.entity.OrderLineItem;
import org.zheng.demo.entity.Product;
import org.zheng.demo.entity.ProductProfile;
import org.zheng.demo.repository.OrderLineItemRepo;
import org.zheng.demo.repository.OrderRepo;
import org.zheng.demo.repository.ProductProfileRepo;
import org.zheng.demo.repository.ProductRepo;

@Service
@Transactional
public class DemoService {

	@Autowired
	private OrderLineItemRepo lineItemRepo;

	@Autowired
	private OrderRepo orderRep;

	@Autowired
	private ProductProfileRepo prodProfRepo;

	@Autowired
	private ProductRepo prodRepo;

	private Order buildOrder(final String orderName) {
		Order order = new Order();
		order.setName(orderName);
		return order;
	}

	private OrderLineItem buildOrderItem(final Order order, final String product, final Integer itemQuantity) {
		OrderItemId itemId1 = new OrderItemId();
		itemId1.setOrderId(order.getId());

		Product prod = findProduct(product);
		itemId1.setProductId(findProduct(product).getId());

		OrderLineItem item = new OrderLineItem();
		item.setId(itemId1);
		item.setOrder(order);
		item.setProduct(prod);
		item.setQuantity(itemQuantity);

		return item;
	}

	public List<OrderLineItem> getOrderItems() {
		List<OrderLineItem> found = lineItemRepo.findAll();
		if (found.isEmpty()) {
			dummyOrderData();
		} else {
			return found;
		}
		return lineItemRepo.findAll();
	}

	private Order saveOrder(final String orderDesc) {
		return orderRep.save(buildOrder(orderDesc));
	}

	private void saveOrderLineItem(final Order order, final String product, final Integer quantity) {
		lineItemRepo.save(buildOrderItem(order, product, quantity));
	}

	private void dummyOrderData() {
		Order order = saveOrder("first order");

		saveOrderLineItem(order, "product 1", 1);
		saveOrderLineItem(order, "product 2", 2);
	}

	private Product saveProduct(final String productName, final String prodDetail) {
		Product prod = new Product();
		prod.setName(productName);

		prod = prodRepo.save(prod);

		ProductProfile prodProfile = new ProductProfile();
		prodProfile.setProduct(prod);
		prodProfile.setProductDetail(prodDetail);

		prodProfRepo.save(prodProfile);

		return prod;
	}

	private Product findProduct(final String prodName) {
		List<Product> found = prodRepo.findByName(prodName);
		if (found.isEmpty()) {
			return saveProduct(prodName, "some Detail");
		}
		return found.get(0);

	}

	public List<ProductProfile> getProductProfiles() {
		List<ProductProfile> found = prodProfRepo.findAll();
		if (found.isEmpty()) {
			saveProduct("prod 1", "prod detail");
		} else {
			return found;
		}
		return prodProfRepo.findAll();
	}
}

5.2 Test Demo Service

In this step, I will create a DemoServiceTest.java to test the getProductProfiles and getOrderItems methods.

DemoServiceTest.java

package org.zheng.demo.controller;

import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.zheng.demo.entity.OrderLineItem;
import org.zheng.demo.entity.ProductProfile;

@SpringBootTest
class DemoServiceTest {

	@Autowired
	private DemoService testService;

	@Test
	void test_getProductProfiles() {
		List<ProductProfile> foundProdProfile = testService.getProductProfiles();
		assertTrue(foundProdProfile.size() > 0);
	}

	@Test
	void test_getOrderItems() {
		List<OrderLineItem> foundOrderLineItems = testService.getOrderItems();
		assertTrue(foundOrderLineItems.size() > 0);
	}

}

Ran the Junit tests and captured the passed results.

Figure 2. Junit Test Results
Figure 2, Passed Test Results

5.3 Demo @RestController

In this step, I will create a DemoController.java class with two RESTful APIs that return the order line items and product profile information.

DemoController.java

package org.zheng.demo.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.zheng.demo.entity.OrderLineItem;
import org.zheng.demo.entity.ProductProfile;

@RestController
public class DemoController {

	@Autowired
	private DemoService testService;

	@GetMapping("/orderItems")
	List<OrderLineItem> getOrderItems() {	
		return testService.getOrderItems();
	}
	
	@GetMapping("/productProfiles")
	List<ProductProfile> getProductProfiles(){	
		return testService.getProductProfiles();
	}

}

5.4 Start the Spring Boot Application

In this step, I will start the spring boot application and capture the server log. The log outlines the database table creation along with the foreign key constraints added by the @MapsId annotation.

server log

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::                (v3.4.0)

2024-12-08T09:41:01.036-06:00  INFO 58456 --- [demo] [           main] org.zheng.demo.DemoApplication           : Starting DemoApplication using Java 17.0.11 with PID 58456 (C:\MaryTools\workspace\demomapsid\bin\main started by azpm0 in C:\MaryTools\workspace\demomapsid)
2024-12-08T09:41:01.042-06:00  INFO 58456 --- [demo] [           main] org.zheng.demo.DemoApplication           : No active profile set, falling back to 1 default profile: "default"
2024-12-08T09:41:01.840-06:00  INFO 58456 --- [demo] [           main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2024-12-08T09:41:01.931-06:00  INFO 58456 --- [demo] [           main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 80 ms. Found 4 JPA repository interfaces.
2024-12-08T09:41:02.691-06:00  INFO 58456 --- [demo] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port 8080 (http)
2024-12-08T09:41:02.709-06:00  INFO 58456 --- [demo] [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2024-12-08T09:41:02.709-06:00  INFO 58456 --- [demo] [           main] o.apache.catalina.core.StandardEngine    : Starting Servlet engine: [Apache Tomcat/10.1.33]
2024-12-08T09:41:02.780-06:00  INFO 58456 --- [demo] [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2024-12-08T09:41:02.782-06:00  INFO 58456 --- [demo] [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1677 ms
2024-12-08T09:41:02.827-06:00  INFO 58456 --- [demo] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2024-12-08T09:41:03.036-06:00  INFO 58456 --- [demo] [           main] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Added connection conn0: url=jdbc:h2:mem:e0372e7a-9c1e-4bda-9091-a636ecf41863 user=SA
2024-12-08T09:41:03.038-06:00  INFO 58456 --- [demo] [           main] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2024-12-08T09:41:03.049-06:00  INFO 58456 --- [demo] [           main] o.s.b.a.h2.H2ConsoleAutoConfiguration    : H2 console available at '/h2-console'. Database available at 'jdbc:h2:mem:e0372e7a-9c1e-4bda-9091-a636ecf41863'
2024-12-08T09:41:03.233-06:00  INFO 58456 --- [demo] [           main] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2024-12-08T09:41:03.306-06:00  INFO 58456 --- [demo] [           main] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 6.6.2.Final
2024-12-08T09:41:03.352-06:00  INFO 58456 --- [demo] [           main] o.h.c.internal.RegionFactoryInitiator    : HHH000026: Second-level cache disabled
2024-12-08T09:41:03.677-06:00  INFO 58456 --- [demo] [           main] o.s.o.j.p.SpringPersistenceUnitInfo      : No LoadTimeWeaver setup: ignoring JPA class transformer
2024-12-08T09:41:03.742-06:00  WARN 58456 --- [demo] [           main] org.hibernate.orm.deprecation            : HHH90000025: H2Dialect does not need to be specified explicitly using 'hibernate.dialect' (remove the property setting and it will be selected by default)
2024-12-08T09:41:03.770-06:00  INFO 58456 --- [demo] [           main] org.hibernate.orm.connections.pooling    : HHH10001005: Database info:
	Database JDBC URL [Connecting through datasource 'HikariDataSource (HikariPool-1)']
	Database driver: undefined/unknown
	Database version: 2.3.232
	Autocommit mode: undefined/unknown
	Isolation level: undefined/unknown
	Minimum pool size: undefined/unknown
	Maximum pool size: undefined/unknown
2024-12-08T09:41:04.662-06:00  INFO 58456 --- [demo] [           main] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
Hibernate: 
    create table t_order (
        id integer generated by default as identity,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    create table t_order_item (
        quantity integer,
        order_id integer not null,
        product_id integer not null,
        primary key (order_id, product_id)
    )
Hibernate: 
    create table t_product (
        id integer generated by default as identity,
        name varchar(255),
        primary key (id)
    )
Hibernate: 
    create table t_product_profile (
        id integer generated by default as identity,
        product_detail varchar(255),
        primary key (id)
    )
Hibernate: 
    alter table if exists t_order_item 
       add constraint FK2y83rerik30vumt2a1mff6606 
       foreign key (order_id) 
       references t_order
Hibernate: 
    alter table if exists t_order_item 
       add constraint FKn4krp2vsjtox6l7sj5h55inx0 
       foreign key (product_id) 
       references t_product
Hibernate: 
    alter table if exists t_product_profile 
       add constraint FKghkpnmy480sm9k7nwayvyj6c1 
       foreign key (id) 
       references t_product
2024-12-08T09:41:04.727-06:00  INFO 58456 --- [demo] [           main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2024-12-08T09:41:05.175-06:00  WARN 58456 --- [demo] [           main] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2024-12-08T09:41:05.642-06:00  INFO 58456 --- [demo] [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port 8080 (http) with context path '/'
2024-12-08T09:41:05.655-06:00  INFO 58456 --- [demo] [           main] org.zheng.demo.DemoApplication           : Started DemoApplication in 5.074 seconds (process running for 5.506)
  • Line 38-41: DDL for T_ORDER table.
  • Line 44-48: DDL for T_ORDER_ITEM table.
  • Line 51-54: DDL for T_PRODUCT table.
  • Line 57-60: DDL for T_PRODUCT_PROFILE table.
  • Line 63-66, 68-71: the two foreign key constraints created by the @MapsId in the T_ORDER_ITEM table.
  • Line 73-76: the foreign key constraint created by @MapsId in the T_PRODUCT_PROFILE table.

You can test the web application by testing both http://localhost:8080/productProfiles and http://localhost:8080/orderItems.

6. Conclusion

In this example, I created a simple Spring web application that defined entities with @MapsId annotation that mapped a field as a foreign key in a one-to-one or many-to-one relationship to another entity. The @MapsId annotation is introduced in JPA 2.0. It’s in the jakarta.persistence package starting from JakartaEE 9 and in the javax.persistence package before JakartaEE 9. Here are key notes about @MapsId annotation:

  • @MapsId maps a specific part of a composite key to a foreign key. E.g. orderId and productId at OrderLineItem defined in step 4.3.
  • @MapsId is used on fields that map relationships. E.g. @ManyToOne or @OneToOne.
  • @MapsId is always used with an @EmbeddedId or @IdClass.

7. Download

This was an example of a gradle project which included @MapsId annotation.

Download
You can download the full source code of this example here: @MapsId Annotation Example

Mary Zheng

Mary graduated from the Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She worked as a lead Software Engineer where she led and worked with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button