HIBERNATE SERIES - CUỐN SỔ
Không Gọi save(), Vì Sao DB Vẫn UPDATE?
PERSISTENCE CONTEXT - XANH = TẦNG PHÓNG TO
Đọc từ trái sang phải. Màu đậm dần nghĩa là gần cơ chế hơn. Dấu
* đánh dấu điểm bài viết khai thác.
Managed - tự phát UPDATE
Load entity trong transaction, sửa field rồi commit; không gọi save() mà SQL vẫn xuất hiện.
Cùng một gốcPersistence context giữ entity và loaded snapshot. Flush so trạng thái hiện tại với snapshot rồi xếp action cần thiết.
Detached - thay đổi bốc hơi
Sửa cùng object sau khi Session đóng; không còn entry và snapshot để dirty-check nên DB đứng yên.
Mức 1 - thời điểm flush
SessionImpl.flush()
Flush tường minh, trước completion hoặc trước query cần đồng bộ
flushBeforeTransactionCompletion()Transaction completion gọi managed flush nếu transaction có thể flush.mustFlushBeforeCompletion()Bỏ qua khi flush mode làMANUALhoặc transaction không ở trạng thái phù hợp.managedFlush()hoặcflush()Cả hai đi vàodoFlush()và phátFlushEvent.autoFlushIfRequired(querySpaces)Đường query chỉ auto-flush khi transaction đang chạy và action có thể ảnh hưởng query-space.FlushMode.MANUALKhông có lời hứa auto-flush trước commit; phải đọc đúng flush mode của Session.
Mức 2 - duyệt cuốn sổ
DefaultFlushEventListener.onFlush()
Biến trạng thái trong persistence context thành execution queue
flushEverythingToExecutions()GọiInterceptor.preFlush(...)- method của interceptor, không phải của listener này - rồi xử lý entity và collection.flushEntities()Lấy snapshot an toàn của cácEntityEntryđang managed.FlushEntityEventMỗi entry hợp lệ được gửi tới nhómFlushEntityEventListener. Đây là cửa vào cột kế.flushCollections()Collection dirty-check có pipeline riêng, không đồng nhất với field scalar.performExecutions()Thực thi các action đã xếp theo thứ tự khóa ngoại, insert, update và delete.postFlush()Cập nhật lại bookkeeping sau khi action queue được thực thi.
Mức 3 - so current với snapshot
DefaultFlushEntityEventListener.onFlushEntity()
Điểm SQL UPDATE được quyết định
entry.requiresDirtyCheck(entity)Loại sớm entity read-only, immutable hoặc chắc chắn không dirty.persister.getValues(entity)Đọc trạng thái hiện tại của object đang managed.entry.getLoadedState()Loaded snapshot là mốc so thông thường; bytecode enhancement có thể cung cấp dấu dirty khác.dirtyCheck(event)Nơi quyết định, đọc được cả ngữ cảnh; nó gọiperformDirtyCheck(event). Đặt breakpoint ở đây, đừng đặt ở interface.persister.findDirty(values, loadedState, ...)Trả về index các property khác snapshot; fallback có thể dùng database snapshot.scheduleUpdate(event)Nếu cần update, thêmEntityUpdateActionvào action queue.- SQL chạy sau dirty-check
save()không phải tác giả của update trên entity đã managed.
Phép kiểm: trong một transaction, load entity, sửa một field và không gọi save(). Bật SQL và breakpoint tại onFlushEntity(); xem loadedState, dirtyProperties và action queue. Lặp lại sau khi đóng EntityManager để xác nhận object detached không còn bị theo dõi.
Dùng đúng unit of work
- Sửa managed entity trong transactionĐể dirty checking tạo update; bỏ lời gọi
save()chỉ mang tính nghi thức. - Kiểm soát thời điểm cần SQLDùng flush tường minh khi thật sự cần DB đồng bộ giữa method, rồi đo tác động.
- Giảm chi phí khi đọcDùng read-only đúng boundary đã kiểm chứng, không tự suy ra từ tên method.
Khi KHÔNG được kết luận
- Flush luôn đồng nghĩa commitFlush đồng bộ state xuống DB trong transaction; rollback sau đó vẫn có thể hủy kết quả.
- Mọi query đều buộc flushVới
AUTO, Hibernate xét transaction và query-space; query không liên quan có thể không flush. - Câu UPDATE chỉ chứa cột đã đổi
findDirtyquyết có update hay không; SQL sinh ra mặc định viết lại cả hàng. Muốn hẹp lại phải khai@DynamicUpdate. - Không có snapshot thì không thể dirty-checkEnhancement, interceptor hoặc database snapshot là các đường khác trong source.