正确使用Session.persist() [英] Correct usage of Session.persist()

查看:112
本文介绍了正确使用Session.persist()的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解 Session.persist()的语义,以及实体管理器用未保存的瞬态实例做什么。我想要实现的只是向会话中添加一个新的临时实例,并在会话刷新时让Hibernate执行 INSERT



我发现如果新实例在同一会话中被保存并被修改,实体管理器将同时生成 INSERT UPDATE 可能导致违反约束的语句。



例如,假设我有一个实体关系 Foo 使用 NOT NULL 和以下服务方法。

  @Transactional 
void persistFoo(String bar){
Foo foo = new Foo();
session.persist(foo);
foo.setBar(bar);

$ / code>

尽管我们为栏提供了一个值,执行这段代码将违反数据库中的 NULL 约束。

  BatchUpdateException:无法将值NULL插入到'bar'列中

Hibernate documentation 声明如下。


persist()使暂时实例持久化。然而,它并不能保证标识符值将立即分配给持久实例,分配可能发生在刷新时间...


INSERT 确实在会话刷新到事务块末尾时执行,但它仍然使用来自对象的属性值进行参数化,如当 persist()被调用时

我知道这个例子说明的特殊问题只是简单的修改而已,但我更感兴趣的是试图理解它是如何工作的。



我的问题是,是这种行为只是 Session.persist()的合约的一部分,还是可以改变?如果是这样,我该如何告诉会话推迟收集生成的 INSERT 语句的参数,直到它被实际执行为止?

Session.persist()的合约的一部分。根据


    hibernate文档
    ,这是执行SQL的顺序。

  1. 更新

  2. 删除集合元素

  3. 插入
  4. 按执行顺序删除






之后发生的更改Session.persist()立即转换为 INSERT 语句会打破此合约,并会在某些使用情况下导致问题。



假设我们有一个 User 实体,并且有可能两个用户以某种方式相互关联。然后我们可以在一个事务中插入两个:

  persist(user1); 
persist(user2);
user1.setPartner(user2);
user2.setPartner(user1);

如果所有内容都存储在 INSERT 那么当持久化 user1 时,我们会得到外键约束违例。



一般而言,通过确保只有状态被传递给 persist 结束于 INSERT ,Hibernate为我们提供了更多灵活性来满足底层数据库约束。



我不知道可以更改此行为的任何配置。当然,正如您所提到的那样,您可以重构代码,以便在设置所有值之后调用 persist ,前提是不违反数据库约束。

I'm trying to understand the semantics of Session.persist(), and exactly what the entity manager does with unsaved transient instances. What I want to achieve is to just add a new transient instance to the session and have Hibernate execute an INSERT when the session is flushed.

I have found that if a new instance is persisted and then modified within the same session, the entity manager will generate both INSERT and UPDATE statements which can lead to constraint violations.

As an example, lets say I have an entity relation Foo with a NOT NULL column bar and the following service method.

@Transactional
void persistFoo(String bar) {
    Foo foo = new Foo();
    session.persist(foo);
    foo.setBar(bar);
}

Despite the fact that we provide a value for bar, executing this code will violate the NULL constraint in the database.

BatchUpdateException: Cannot insert the value NULL into column 'bar'

The Hibernate documentation states the following.

persist() makes a transient instance persistent. However, it does not guarantee that the identifier value will be assigned to the persistent instance immediately, the assignment might happen at flush time...

The INSERT is indeed executed at the point the session is flushed at the end of the transactional block, but it is still parameterised with property values from the object as it was when persist() was called.

I know the particular problem this example illustrates can be fixed with some simple changes, but I'm more interested in trying to understand how its supposed to work.

My question is, is this behaviour just part of the contract of Session.persist() or can it be changed? If so, how do I tell the session to defer gathering parameters for the generated INSERT statement until the time when it is actually executed?

解决方案

Yes, this is part of the contract of Session.persist(). According to Hibernate documentation, this is the order in which SQLs are executed:

  1. Inserts, in the order they were performed
  2. Updates
  3. Deletion of collection elements
  4. Insertion of collection elements
  5. Deletes, in the order they were performed

This order is part of the official Hibernate API, and applications rely on it when manipulating their entity graphs.

Putting changes that occurred after Session.persist() immediately to INSERT statements would break this contract and would cause problems in some use cases.

Let's suppose that we have a User entity, and it is possible that two users are associated with each other in some way. Then we can insert the two in one transaction:

persist(user1);
persist(user2);
user1.setPartner(user2);
user2.setPartner(user1);

If everything was stored in INSERT statements, then we would get foreign key constraint violation when persisting user1.

In general, by ensuring that only the state that was passed to persist ends up in INSERT, Hibernate gives us more flexibility to satisfy underlying DB constraints.

I am not aware of any config with which this behavior can be changed. Of course, as you mentioned, you can restructure your code so that persist is called after all the values are set, provided that no DB constraints are violated.

这篇关于正确使用Session.persist()的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆