只有單向收集更改將傳播到父實體版本as explained in this article。因爲您正在使用雙向關聯,所以控制此關聯的是@ManyToOne
,因此,在父集合中添加/刪除實體不會影響父實體版本。
但是,您仍然可以將更改從子實體傳播到父實體。這需要您每當修改子實體時傳播OPTIMISTIC_FORCE_INCREMENT鎖。
This article詳細解釋了你應該實現這種用例的方式。
總之,你需要把所有的實體實施RootAware
接口:
public interface RootAware<T> {
T root();
}
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
private Long id;
private String title;
@Version
private int version;
//Getters and setters omitted for brevity
}
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment
implements RootAware<Post> {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
private String review;
//Getters and setters omitted for brevity
@Override
public Post root() {
return post;
}
}
@Entity(name = "PostCommentDetails")
@Table(name = "post_comment_details")
public class PostCommentDetails
implements RootAware<Post> {
@Id
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId
private PostComment comment;
private int votes;
//Getters and setters omitted for brevity
@Override
public Post root() {
return comment.getPost();
}
}
然後,你需要兩個事件偵聽器:
public static class RootAwareInsertEventListener
implements PersistEventListener {
private static final Logger LOGGER =
LoggerFactory.getLogger(RootAwareInsertEventListener.class);
public static final RootAwareInsertEventListener INSTANCE =
new RootAwareInsertEventListener();
@Override
public void onPersist(PersistEvent event) throws HibernateException {
final Object entity = event.getObject();
if(entity instanceof RootAware) {
RootAware rootAware = (RootAware) entity;
Object root = rootAware.root();
event.getSession().lock(root, LockMode.OPTIMISTIC_FORCE_INCREMENT);
LOGGER.info("Incrementing {} entity version because a {} child entity has been inserted", root, entity);
}
}
@Override
public void onPersist(PersistEvent event, Map createdAlready)
throws HibernateException {
onPersist(event);
}
}
和
public static class RootAwareInsertEventListener
implements PersistEventListener {
private static final Logger LOGGER =
LoggerFactory.getLogger(RootAwareInsertEventListener.class);
public static final RootAwareInsertEventListener INSTANCE =
new RootAwareInsertEventListener();
@Override
public void onPersist(PersistEvent event) throws HibernateException {
final Object entity = event.getObject();
if(entity instanceof RootAware) {
RootAware rootAware = (RootAware) entity;
Object root = rootAware.root();
event.getSession().lock(root, LockMode.OPTIMISTIC_FORCE_INCREMENT);
LOGGER.info("Incrementing {} entity version because a {} child entity has been inserted", root, entity);
}
}
@Override
public void onPersist(PersistEvent event, Map createdAlready)
throws HibernateException {
onPersist(event);
}
}
這你可以註冊如下:
public class RootAwareEventListenerIntegrator
implements org.hibernate.integrator.spi.Integrator {
public static final RootAwareEventListenerIntegrator INSTANCE =
new RootAwareEventListenerIntegrator();
@Override
public void integrate(
Metadata metadata,
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
final EventListenerRegistry eventListenerRegistry =
serviceRegistry.getService(EventListenerRegistry.class);
eventListenerRegistry.appendListeners(EventType.PERSIST, RootAwareInsertEventListener.INSTANCE);
eventListenerRegistry.appendListeners(EventType.FLUSH_ENTITY, RootAwareUpdateAndDeleteEventListener.INSTANCE);
}
@Override
public void disintegrate(
SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
//Do nothing
}
}
,然後通過一個Hibernate配置屬性提供RootAwareFlushEntityEventListenerIntegrator
:
configuration.put(
"hibernate.integrator_provider",
(IntegratorProvider)() -> Collections.singletonList(
RootAwareEventListenerIntegrator.INSTANCE
)
);
現在,當你修改PostCommentDetails
實體:
PostCommentDetails postCommentDetails = entityManager.createQuery(
"select pcd " +
"from PostCommentDetails pcd " +
"join fetch pcd.comment pc " +
"join fetch pc.post p " +
"where pcd.id = :id", PostCommentDetails.class)
.setParameter("id", 2L)
.getSingleResult();
postCommentDetails.setVotes(15);
父Post
實體版本的修改,以及:
SELECT pcd.comment_id AS comment_2_2_0_ ,
pc.id AS id1_1_1_ ,
p.id AS id1_0_2_ ,
pcd.votes AS votes1_2_0_ ,
pc.post_id AS post_id3_1_1_ ,
pc.review AS review2_1_1_ ,
p.title AS title2_0_2_ ,
p.version AS version3_0_2_
FROM post_comment_details pcd
INNER JOIN post_comment pc ON pcd.comment_id = pc.id
INNER JOIN post p ON pc.post_id = p.id
WHERE pcd.comment_id = 2
UPDATE post_comment_details
SET votes = 15
WHERE comment_id = 2
UPDATE post
SET version = 1
where id = 1 AND version = 0
PostComment
實體也是如此。
如果你插入一個新的子實體它的工作原理,甚至:
Post post = entityManager.getReference(Post.class, 1L);
PostComment postComment = new PostComment();
postComment.setId(3L);
postComment.setReview("Worth it!");
postComment.setPost(post);
entityManager.persist(postComment);
休眠管理來增加適當父實體:
SELECT p.id AS id1_0_0_ ,
p.title AS title2_0_0_ ,
p.version AS version3_0_0_
FROM post p
WHERE p.id = 1
INSERT INTO post_comment (post_id, review, id)
VALUES (1, 'Worth it!', 3)
UPDATE post
SET version = 3
WHERE id = 1 AND version = 2
刪除子實體時,它也可以工作:
PostComment postComment = entityManager.getReference(PostComment.class, 3l);
entityManager.remove(postComment);
Hibernate管理增加本用例中的父實體:
SELECT pc.id AS id1_1_0_ ,
pc.post_id AS post_id3_1_0_ ,
pc.review AS review2_1_0_
FROM post_comment pc
WHERE pc.id = 3
SELECT p.id AS id1_0_0_ ,
p.title AS title2_0_0_ ,
p.version AS version3_0_0_
FROM post p
WHERE p.id = 1
DELETE FROM post_comment
WHERE id = 3
UPDATE post
SET version = 4
WHERE id = 1 and version = 3
請注意,這隻會增加家長的版本時,孩子們都加入或將其從集合中移除,而不是當一個孩子的屬性被修改(我會找到有用的,以保證整體的完整性實體彙總)。 –