@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 theID
column.T_PRODUCT
: the primary key is theID
column.T_ORDER_ITEM
: the primary key is the composite key ofORDER_ID
andPRODUCT_ID
. Both are foreign keys to theT_ORDER
andT_PRODUCT
tables with the many-to-one relationship.T_PRODUCT_PROFILE
: the primary key is theID
column, which is also the foreign key to theT_PRODUCT
table with the one-to-one relationship.
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 theProduct
entity. - Line 24: the
@OneToOne
annotation indicates thatProduct
andProductProfile
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 theorderId
field as the foreign key to theOrder
entity’s primary key with the@ManyToOne
annotation. - Line 26:
@MapsId
annotation maps theproductId
field as the foreign key to theProduct
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.
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 theT_ORDER_ITEM
table. - Line 73-76: the foreign key constraint created by
@MapsId
in theT_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
andproductId
atOrderLineItem
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.
You can download the full source code of this example here: @MapsId Annotation Example