Overview:
Trong nhiều dự án, bạn có thể sẽ gặp phải vấn đề cần mapping nhiều entities vào 1 table duy nhất. Vậy làm thế nào để thực hiện điều này với JPA. Trong bài này mình sẽ giới thiệu cách map nhiều entity vào 1 table. Sử dụng multiple entities có thể giúp tăng tốc xử lý đọc và ghi xuống database.
Trong bài viết này mình sẽ sử dụng database là PostgreSQL
Model
Ở đây mình có 1 table Bill define như bên dưới:
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 ) |
Ở đây mình có thông tin của 1 bill như trên, giờ mình sẽ mapping table bill này với 2 entities BillEntity và TransactionEntity, trước hết mình define 1 entity gọi là 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 là 1 class abstract, nó sẽ chưa những attribute base dùng chung giữa BillEntity và TransactionEntity, BillEntity sẽ extend từ BaseBill trong khi đó TransactionEntity ngoài những attribute base thì còn chưa thêm thông tin ngày giao dịch(transaction_date)
.
class abstract BaseBill sử dụng anotation @MappedSuperclass
để những class BillEntity và TransactionEntity có thể kế thừa các attribute của BaseBill. Để tìm hiểu thêm về @MappedSuperclass
thì bạn có thể tham khảo tại đây.
OK, bây giờ chúng ta sẽ thao tác thử với database xem thế nào. Ở đây mình sẽ implement 2 function để create BillEntity và 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') |
Như vậy ta thấy ở method create TransactionEntity, transaction_date cũng đã được insert vào 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 |
Ở class abstract BaseBill ta đã define company_id là @NaturalId. Nên ta có thể fetch được data bằng NaturalId
.
Define 1 method để get thông qua 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
Như vậy, qua bài viết trên, mình đã giới thiệu cách map multiple entities với 1 table trong database, hy vọng bài viết này hữu ích với mọi người
https://vladmihalcea.com/map-multiple-jpa-entities-one-table-hibernate/