Overview:
In many projects, you may have problems mapping multiple entities into a single table. So how to accomplish this with JPA. In this article I will introduce how to map multiple entities into one table. Using multiple entities can help speed read and write operations to the database. In this article I will use the PostgreSQL database
Model
Here I have a Bill define table as below:
1 2 3 4 5 6 7 8 9 | CREATE TABLE public.bill ( id bigint NOT NULL DEFAULT nextval('bill_id_seq'::regclass), company_id character varying COLLATE pg_catalog."default", quantity bigint, amount bigint, transaction_date date ) |
Here I have the information of 1 bill as above, now I will mapping this bill with 2 entities BillEntity and TransactionEntity, firstly I define an entity called BaseBill:
BaseBill:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | @MappedSuperclass public abstract class BaseBill<T extends BaseBill> { @Id @GeneratedValue(strategy= GenerationType.IDENTITY) private Long id; @NaturalId @Column(name = "company_id") private Long companyId; @Column private Long amount; @Column private Long quantity; public Long getId() { return id; } public T setId(Long id) { this.id = id; return (T) this; } public Long getCompanyId() { return companyId; } public T setCompanyId(Long companyId) { this.companyId = companyId; return (T) this; } public Long getAmount() { return amount; } public T setAmount(Long amount) { this.amount = amount; return (T) this; } public Long getQuantity() { return quantity; } public T setQuantity(Long quantity) { this.quantity = quantity; return (T) this; } } |
BillEntity:
1 2 3 4 5 6 | @Entity(name = "BillEntity") @Table(name = "bill") @DynamicUpdate public class BillEntity extends BaseBill<BillEntity> { } |
TransactionEntity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | @Entity(name = "TransactionEntity") @Table(name = "bill") public class TransactionEntity extends BaseBill<TransactionEntity> { @Column(name = "transaction_date") private LocalDate transactionDate; public LocalDate getTransactionDate() { return transactionDate; } public TransactionEntity setTransactionDate(LocalDate transactionDate) { this.transactionDate = transactionDate; return this; } } |
BaseBill is an abstract class. The abstract BaseBill class uses anotation @MappedSuperclass
so that the BillEntity and TransactionEntity classes can inherit the BaseBill attributes. To learn more about @MappedSuperclass
, you can refer here .
OK, now we will try to manipulate the database to see how. Here I will implement 2 functions to create BillEntity and TransactionEntity:
Create BillEntity:
1 2 3 4 5 6 7 8 9 10 | @Transactional public void createBill() { entityManager.persist( new BillEntity() .setCompanyId("111-5555555") .setAmount(200000L) .setQuantity(1000L) ); } |
Result log:
1 2 3 4 5 6 7 8 9 10 11 | Hibernate: insert into bill (amount, company_id, quantity) values (?, ?, ?) Hibernate: select currval('bill_id_seq') |
create TransactionEntity
1 2 3 4 5 6 7 8 9 10 | @Transactional public void createTransaction() { entityManager.persist( new TransactionEntity() .setCompanyId("222-5555555") .setAmount(300000L) .setQuantity(2000L) .setTransactionDate(LocalDate.now())); } |
Result log:
1 2 3 4 5 6 7 8 9 10 11 | Hibernate: insert into bill (amount, company_id, quantity, transaction_date) values (?, ?, ?, ?) Hibernate: select currval('bill_id_seq') |
So we see in the create TransactionEntity method, the transaction_date has also been inserted into the database. Result of database:
1 2 3 4 5 | id | quantity | amount | transaction_date | company_id ----+----------+--------+------------------+------------- 1 | 1000 | 200000 | | 111-5555555 2 | 2000 | 300000 | 2019-12-24 | 222-5555555 |
In abstract BaseBill class we have defined company_id as @NaturalId . So we can fetch data using NaturalId
. Define 1 method to get via naturalId:
1 2 3 4 5 6 7 8 | public TransactionEntity getTrasactionEntity() { TransactionEntity bookSummary = entityManager .unwrap(Session.class) .bySimpleNaturalId(TransactionEntity.class) .load("222-5555555"); return bookSummary; } |
result:
1 2 3 4 5 6 7 | { "id":"2", "companyId":"222-5555555", "amount":300000, "quantity":2000 } |
Conclution
So, through the above article, I introduced how to map multiple entities with 1 table in the database, hoping this article will be helpful to everyone.
https://vladmihalcea.com/map-multiple-jpa-entities-one-table-hibernate/