Hibernate在实体未更改的情况下,在刷新期间持久化实体

Hibernate在实体未更改的情况下,在刷新期间持久化实体,hibernate,jdbc,entity,Hibernate,Jdbc,Entity,我遇到了一个问题,实体经理正在持久化一个我认为在刷新期间没有更改的实体 我知道下面的代码是问题所在,因为如果我注释掉它,实体就不会持久存在。在这段代码中,我所做的就是加载实体并调用一些getter Query qry = em.createNamedQuery("Clients.findByClientID"); qry.setParameter("clientID", clientID); Clients client = (Clients) qry.getSingleResult();

我遇到了一个问题,实体经理正在持久化一个我认为在刷新期间没有更改的实体

我知道下面的代码是问题所在,因为如果我注释掉它,实体就不会持久存在。在这段代码中,我所做的就是加载实体并调用一些getter

Query qry = em.createNamedQuery("Clients.findByClientID");
qry.setParameter("clientID", clientID);

Clients client = (Clients) qry.getSingleResult();

results.setFname(client.getFirstName());
results.setLname(client.getLastName());
...
return results;
稍后在另一个方法中,我执行另一个namedQuery,这会导致实体管理器刷新。由于某些原因,上面加载的客户端被持久化

这是一个问题的原因,因为在所有这些中间,有一些旧的代码正在对客户端进行一些直接的JDBC更改。当实体管理器持久化时,直接JDBC所做的更改将丢失

我们目前的理论是,实体经理将实体与底层记录进行比较,发现它们不同,然后将其保留下来


有人能解释或证实我们看到的行为吗?

我不知道冬眠的内脏,但我猜你是对的。但我不认为hibernate与db相比,而是与本地会话缓存相比

您可以通过将对象设置为只读来避免此问题-hibernate然后不检查更改,例如

   session.setReadOnly(client, true);

或者,也可以使用Session.execute()逐出对象。下次需要该对象时,它将从数据库中读取,包括您使用自定义JDBC所做的任何更改。

您是对的,实体管理器会比较这些对象(只要它们在会话中不是只读的、被逐出的、调用了session.clear()等),如果它们不匹配,则会将它们清除

t这个特定的问题以前也曾困扰过我们,并导致了完全相同的不必要的刷新问题--我们的问题也是一个性能问题,在一个紧密的循环中,我们需要
flush()
会话,每次我们都会将数百个未更改的对象转储回数据库

在我们的例子中,所讨论的实体没有正确地实现
equals()
hashCode()
——Hibernate显然依赖于这些实体来检查对象的更改,如果它们在某些方面不正确,那么它别无选择,只能将它们刷新回来

从记忆中(那是很久以前)它实际上是一个没有正确实现
hashCode()
的附属实体——想象一个
Student
-
Teacher
关系,其中
Teacher
没有正确实现这些方法。当脏检
学生
时,Hibernate在其实体缓存中检查
教师
,认为它不在那里(由于不正确的实现),因此认为
学生
已更改,将其重新推回以写出新的
教师
ID


因此,请检查
hashCode()
equals()
以查找所讨论的实体、其所有相关实体以及写入数据库的任何其他组件/用户类型。

如果getter正在执行任何逻辑,因此它返回的不是hibernate发送给setter的内容,那么它将被标记为脏。例如,当hibernate从数据库中给您一个null值时,如果您的getter逻辑返回0,那么它将被标记为dirty,因为它看起来已经更改了。我不知道有什么方法可以告诉hibernate不要将该更改标记为使实体变脏。

如果它与本地会话缓存进行比较,那么jdbc调用如何与它交互。jdbc应该直接进入db并跳过会话(或者我是这么认为的)。我正在注入数据源,这会有区别吗?如果您的jdbc层使用相同的数据源,并且数据源是SingleConnectionDataSource,那么如果hibernate调用在jdbc调用之前刷新,那么它们会进行交互。您如何确定这是hashcode或equals的不正确实现?不幸的是,进行了大量非常痛苦的调试。我想不起来在我们能够打开的Hibernate中是否有有用的日志记录;我认为最终,我们只通过Hibernate代码中的几个战略性断点就解决了这个问题。从DefaultFlusEntityEventListener.dirtyCheck()开始——它会查找所有脏属性,因此应该能够清楚地知道哪些属性导致了反冲洗。实际上,我们最终编写了一个JUnit测试用例,它爬网出类路径,找到所有实体,并检查它们在hashCode()和equals()方面的行为是否正确,这也发现了其他一些。我们有一个自定义用户类型,它将数据库中的日期从0000-00-00转换为null。我们怀疑这可能是设置了肮脏的标志。按照您的建议使用dirtyCheck,我们应该能够验证这是否是源代码。如果是。。。还有什么话要说,不要让这一变化成为肮脏的标志吗?我认为你不能排除它。我怀疑发生的是,bean的getter没有返回Hibernate期望的值——也就是说,Hibernate在bean中看到“null”,但在数据库状态中看到“0000-00-00”。不是100%确定比较是如何完成的,但是如果您逐步完成,DefaultFlusEntityEventListener中的代码应该可以帮助您解决这个问题。事实证明,我们的Equals实现是错误的。一旦我们得到正确的逻辑,该实体意外地停止向db写入。为此,我收到了错误消息“应用程序试图编辑只读项://无法写入只读对象”,您的帖子为我指明了正确的方向。也就是说,我在二传手上做了一个防守副本,并在接球手上返回了一个新的树集(原因尚不清楚)。在消除了这种逻辑之后,一切都很好!这是一个一旦从经验中学到就很难忘记的东西:)