JPA SERIES - QUAN HỆ
Hai Field Java, Một FK: Chỉ Owning Side Cầm Bút
SOURCE OF TRUTH - XANH = TẦNG PHÓNG TO
mappedBy khai báo collection là inverse side; nó không biến hai field Java thành một field tự đồng bộ.Chỉ sửa inverse side
order.getItems().add(item); collection đổi nhưng item.order vẫn null.
Một source of truth cho FKVới FK
order_id ở bảng con, field OrderItem.order là owning side quyết định giá trị cột.Đồng bộ cả hai phía
items.add(item) và item.setOrder(this); mô hình RAM nhất quán với FK cần ghi.
Mức 1 - boot mapping
CollectionBinder.bind()
mappedBy trở thành metadata inverse
- Đọc
@OneToMany(mappedBy="order")Binder lưu tên property ở entity đích. isUnownedCollection()Trả true khimappedBy != null.collection.setInverse(true)Collection được đánh dấu không sở hữu association mapping.bindStarToManySecondPass()Resolve propertyordervà kiểm type của phía được trỏ tới.- Không được khai join column ở inverse sideMetadata từ chối các khai báo mâu thuẫn với
mappedBy.
Mức 2 - flush hai state độc lập
DefaultFlushEntityEventListener
Hibernate đọc field của child khi quyết định FK
- Collection cha dirtyAdd vào wrapper làm collection state thay đổi và cascade có thể persist child.
AbstractCollectionPersister.isInverse()Persister biết collection không sở hữu row update cho association.- Flush entity con
persister.getValues(item)đọc fielditem.order. item.order == nullGiá trị owning side này được bind vào cột FK của child.INSERT ... order_id = NULLCascade đã tạo row con nhưng không bịa quan hệ từ inverse collection.
Mức 3 - giữ model nhất quán
Helper Method Trên Aggregate
Một thao tác nghiệp vụ cập nhật cả hai lời khai trong RAM
order.addItem(item)Đặt mutation vào một API thay vì rải hai lệnh ở caller.items.add(item)Giữ phía đọc/traverse của aggregate đúng ngay trong RAM.item.setOrder(this)Đặt owning side để FK được ghi đúng lúc flush.removeItem(item)Gỡ collection và clear owning reference theo cùng invariant.- Cascade khác quyền sở hữu FK
cascadetruyền operation;orphanRemovalquyết định delete; cả hai không đổi owning side.
Phép kiểm: trong cùng transaction, log item.getOrder(), bật SQL và thử ba case: chỉ add collection, chỉ set child, dùng helper hai phía. Sau flush, đối chiếu object graph và cột order_id.
Giữ invariant tại một chỗ
- Dùng helper add/remove hai phíaCaller không thể quên owning reference.
- Viết test cả RAM lẫn DBAssert hai field trước flush và FK sau reload.
- Chọn aggregate API rõ nghĩaKhông expose collection mutable nếu invariant quan hệ quan trọng.
Khi KHÔNG được kết luận
- Inverse side hoàn toàn vô hình với HibernateNó còn semantics cascade, dirty collection và orphan removal; chỉ không cầm quyền ghi FK này.
cascade = ALLtự set hai phíaCascade truyền lifecycle operation, không sửa object reference hộ ứng dụng.- Owning side luôn là phía chaOwning side do mapping của FK/join table quyết định, không theo tên parent/child.