2010-05-24 66 views
4

考虑两个实体父和子。休眠版本化父实体

  • 孩子是父母的短暂集合的一部分
  • 孩子有多对一映射于母公司与FetchType.LAZY

两者都显示相同的形式向用户上。当用户保存数据时,我们首先更新父实例,然后更新子集合(都使用合并)。

现在到了棘手的部分。当用户只修改表单上的Child属性时,hibernate脏检查不会更新父实例,因此不会增加该实体的乐观锁定版本号。

我希望看到情况,其中只有Parent是版本化的,每当我调用Parent的合并时,即使实际更新未在db中执行,版本也会始终更新。

+0

在这种情况下,您可能想回答自己的问题。有人可能会来,并为此投票。或者更好的是,这可能会帮助正在寻找类似事物的人。 – 2010-05-24 08:18:42

回答

3

我想我想通了。合并后被调用一个附加的实例引用被返回。当我使用entityManager.lock(更新,LockModeType.WRITE)获得显式锁定时;那么即使父实例未在db中更新,版本号也会增加。

另外,我将分离的实例版本与持久实例版本进行比较。如果它们不匹配,那么Parent在db中被更新,并且版本号也发生了变化。这使版本号保持一致。否则,即使合并操作改变了它,entityManager.lock也会增加版本号。

仍在寻找解决方案当合并期间实体不脏时,如何使hibernate增加版本。

+0

+1用于锁定解决方案 – oedo 2010-05-24 09:10:15

0

我不认为你可以强迫hibernate增加一个未更改对象的版本号,因为如果没有任何变化(出于显而易见的原因),它将不会执行任何db UPDATE查询。

你可以做一个讨厌的黑客,如添加一个新的领域的对象,手动增加,但个人,这似乎是浪费时间和资源。我会去你的明确的锁定解决方案,因为这似乎给你你想要的,没有不必要的嘲弄。

1

您可以将更改从子实体传播到父实体。这需要您每当修改子实体时传播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