Java Hibernate版本控制父实体
考虑两个实体:父实体和子实体Java Hibernate版本控制父实体,java,hibernate,jpa,transactions,locking,Java,Hibernate,Jpa,Transactions,Locking,考虑两个实体:父实体和子实体 子对象是父对象的临时集合的一部分 子项具有到具有FetchType.LAZY的父项的多通映射 对用户而言,这两个选项都显示在同一表单上。当用户保存数据时,我们首先更新父实例,然后更新子集合(都使用合并) 现在是棘手的部分。当用户修改表单上的独子属性时,hibernate脏检查不会更新父实例,因此不会增加该实体的乐观锁定版本号 我希望看到这样的情况,即只有父级进行版本控制,并且每次我为父级调用merge时,版本总是会更新,即使实际的更新没有在数据库中执行。我想我已
- 子对象是父对象的临时集合的一部分
- 子项具有到具有FetchType.LAZY的父项的多通映射
我希望看到这样的情况,即只有父级进行版本控制,并且每次我为父级调用merge时,版本总是会更新,即使实际的更新没有在数据库中执行。我想我已经解决了这个问题。调用合并后,将返回一个附加的实例引用。当我使用entityManager.lock(更新,LockModeType.WRITE)获取该文件的显式锁时;即使父实例未在数据库中更新,版本号也会增加 此外,我正在比较分离实例版本和持久化实例版本。如果它们不匹配,则在db中更新了父级,并且版本号也已更改。这使版本号保持一致。否则,即使合并操作更改了entityManager.lock的版本号,它也会增加版本号
仍在寻找解决方案,如何使hibernate在合并期间在实体不脏的情况下增加版本。我认为您不能强制hibernate增加未更改对象的版本号,因为如果没有任何更改(出于明显的原因),它将不会执行任何db
更新
查询
你可以做一个令人讨厌的黑客行为,比如向对象添加一个新字段并手动递增,但就个人而言,这似乎是在浪费时间和资源。我会选择您的显式锁定解决方案,因为它似乎可以满足您的需求,而无需进行不必要的黑客操作。您可以将更改从子实体传播到父实体。这要求您在修改子实体时传播锁 因此,您需要让所有实体实现一个接口:
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 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
我刚刚实现了一个类似的东西,它像beast一样快速而漂亮地工作。 在你要救你的“孩子”的时候,打电话做一些类似的事情:
save(child);
T parent = child.getParentEntity();
entityManager.lock(parent, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
您需要访问实体管理器,该管理器可以在spring中获得,如:
@PersistenceContext
private EntityManager entityManager;
您的父实体应该具有来自javax.persistence.Version的@Version,而不是spring实体。(我假设在孩子保存时刻,当你保存孩子时,你将完成所有的验证和事情,父母肯定会弄脏)在这种情况下,你可能想回答你自己的问题。也许有人会来投票给你。或者更好,这可能会帮助正在寻找类似东西的人。如何在spring配置中实现这一点。put(“hibernate.integrator_provider”,(IntegratorProvider)(->Collections.singletonList(rootawareeventlisteneregulator.INSTANCE));
hibernate.integrator\u提供程序
属性也可以采用完全限定的类名,因此您也可以通过Spring提供。只需将逻辑(当前表示为lambda)封装在一个专用类中即可。由于此解决方案在技术上是正确的,并生成正确的db状态,因此hibernate中仍然存在一个bug。(). 乐观锁增量在事务提交时验证。因此,如果您想将正确的版本返回给事务之外的调用方,那么它基本上是无用的。我认为这是web服务的常见情况。这个问题与手动调用flush有关,这是一种代码味道。无论如何,在提交之前都会调用Flush,以呈现预期的结果。但是,Session.Flush和Transaction.commit之间存在差异。使用flush时,只有语句将在DB级别执行,但commit将在DB端触发commit。因此,如果在刷新后执行某些代码并触发异常,则刷新的命令仍将回滚。当事务已经提交时,情况并非如此@Vladmichalcea:您是否知道如何从方法返回正确的版本号,以及如何对所有代码行具有回滚意识?
save(child);
T parent = child.getParentEntity();
entityManager.lock(parent, LockModeType.OPTIMISTIC_FORCE_INCREMENT);
@PersistenceContext
private EntityManager entityManager;