我知道瞬态实例意味着该实例是新创建的,并且其对应的行在数据库中不存在,因为分离的实例在数据库中有相应的条目,但目前与任何会话都没有关联。
是否有任何方法可以使用 Session 上的任何方法或以编程方式在休眠中使用其他方法来识别对象是瞬态的或分离的?
我知道瞬态实例意味着该实例是新创建的,并且其对应的行在数据库中不存在,因为分离的实例在数据库中有相应的条目,但目前与任何会话都没有关联。
是否有任何方法可以使用 Session 上的任何方法或以编程方式在休眠中使用其他方法来识别对象是瞬态的或分离的?
听起来您正在寻找EntityManager#contains(Object)
.
检查实例是否是属于当前持久性上下文的托管实体实例。
为了检查对象 e 是否在:-
持久性上下文:- EntityManager.contains(e) 应该返回 true。
分离状态:PersistenceUnitUtil.getIdentifier(e) 返回实体标识符属性的值。
瞬态:- PersistenceUnitUtil.getIdentifier(e) 返回 null
您可以从 EntityManagerFactory 访问 PersistenceUnitUtil。
有两个问题需要注意。首先,请注意,在刷新持久性上下文之前,标识符值可能不会被分配和可用。其次,如果您的标识符属性是原语(长而不是长),Hibernate(与其他一些 JPA 提供程序不同)永远不会从 Persistence-UnitUtil#getIdentifier() 返回 null。
Matt 建议的内容只会检查给定对象是否是瞬态的或与某个实体管理器相关联。
如果您想检查它是分离的还是瞬态的(您不应该真正关心它!它应该是透明的),您需要检查给定对象是否具有 ID。
if(data.getID() == null) return TRANSIENT;
只能为持久/分离的对象设置 ID。如果由于某种原因您自己在瞬态对象上设置 ID,那么我认为您想要做的事情是不可能的。
如果您不知道哪个字段是 ID(出于某种原因)或者您想使其通用,您可以尝试:
ClassMetadata metadata = HibernateUtil.getSessionFactory().getClassMetadata(data.getClass());
if(metadata.getIdentifier(data) == null) return TRANSIENT;
您可以尝试将瞬态对象和分离对象传递给hibernate会话更新方法并观察差异。对于瞬态对象,hibernate会报错。
所以休眠知道对象是瞬态的或分离的。但是怎么做?答案很简单:hibernate 会在更新前做一个 select 来获取哪些字段是脏的信息。如果对象是瞬态的,则该选择操作将没有结果,然后休眠知道这是瞬态对象并报告错误。
或者,如果你使用@SelectBeforeUpdate(false)
,hibernate不会做选择,而是直接更新,在这种情况下,jdbc会因为需要更新的行不存在而报错。
当然,您可以检查 id 字段是否由数据库生成,否则了解其状态的唯一方法是进行查询:未找到意味着瞬态。
正如其他人所说,org.hibernate.Session.contains(Object)
可以在 Hibernate 中使用来了解实体实例是附加还是分离。关于瞬态,我认为可以做的最好的事情是org.hibernate.Session.saveOrUpdate(Object)
(凭借org.hibernate.persister.entity.AbstractEntityPersister.isTransient(Object, SessionImplementor)
)所做的,即:
@Id
属性,它总是被认为是瞬态的@Id
属性值为 ,null
则始终将其视为瞬态@Id
属性值等于配置的unsaved-value
,那么它被认为是瞬态的(仅适用于 XML 映射,我认为它有些过时,请参见此处)@Version
属性,其值为新创建的实例之一(也就是说,它是 anull
Long
或Date
等),那么它被认为是瞬态的因此,我认为,对于没有生成 id 的实体,查看@Version
属性是要走的路。如果没有这样的属性,Hibernate 本身saveOrUpdate
会对数据库进行 SELECT 查询,以确定实体实例是否是瞬态的,以确定它是否应该分别执行 INSERT 或 UPDATE。
请参阅 Hibernate 手册(例如,此处为 4.3 之一)。