java 休眠拦截器 - 为什么在 onSave 之后调用 onFlushDirty?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25155868/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Hibernate Interceptors - Why is onFlushDirty called after onSave?
提问by Stefan
The Plan
计划
I am using Hibernate to implement createDate and lastUpdate Timestamps for a little project. I use an EmptyInterceptor and overload the provided methods based on the proposed solution i found here. The solution works fine unless one little detail. I want to add a column indicating if an object has been updated already. I know I could achieve this by simply comparing if there is a difference in the two created and updated timestamps, but I need to have this field indicating that there was an update.
我正在使用 Hibernate 为一个小项目实现 createDate 和 lastUpdate Timestamps。我使用 EmptyInterceptor 并根据我在此处找到的建议解决方案重载提供的方法。除非一个小细节,否则该解决方案工作正常。我想添加一列,指示对象是否已更新。我知道我可以通过简单地比较两个创建和更新的时间戳是否存在差异来实现这一点,但我需要让这个字段指示有更新。
I use the onSave method which gets called when a new object is stored to set the wasUpdated value to 'N', indicating that there was no update. In the onFlushDirty() method I set this value to 'Y'.
我使用在存储新对象时调用的 onSave 方法将 wasUpdated 值设置为“N”,表示没有更新。在 onFlushDirty() 方法中,我将此值设置为“Y”。
The Problem
问题
I would expext that when I create and persist a new Object, that the createDate and the lastUpdate fields have the same Date, but the wasUpdated field is set to 'N' as there was no update. I only use session.save() in my code, there is no session.update() and also no session.saveOrUpdate(). The logs of Hibernate indicate that there is actually an Update, which sets the wasUpdated value to 'Y'.
我会说,当我创建并持久化一个新对象时,createDate 和 lastUpdate 字段具有相同的日期,但是 wasUpdated 字段设置为“N”,因为没有更新。我只在我的代码中使用 session.save(),没有 session.update() 也没有 session.saveOrUpdate()。Hibernate 的日志表明实际上有一个更新,它将 wasUpdated 值设置为“Y”。
What can be the source of this update? Where is it triggered?
此更新的来源是什么?在哪里触发?
Object initialization and persistence
对象初始化和持久化
I disabled auto-commit in the hibernate.cfg.xml.
我在 hibernate.cfg.xml 中禁用了自动提交。
<property name="hibernate.connection.autocommit">false</property>
This is how I create the object:
这就是我创建对象的方式:
ExampleObject ex = new ExampleObject();
ex.setValue("TestStringValue");
this.session = HibernateUtil.getSessionFactory().openSession();
this.session.beginTransaction();
this.session.save(ex);
this.session.getTransaction().commit();
this.session.close();
The Interceptor
拦截器
@Override
public boolean onSave(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
if (entity instanceof TimeStamped) {
Date insertDate = new Date();
int indexOfCreateDateColumn = ArrayUtils.indexOf(propertyNames, "createdDate");
int indexOfUpdatedDateColumn = ArrayUtils.indexOf(propertyNames, "lastUpdatedDate");
int indexOfWasUpdated = ArrayUtils.indexOf(propertyNames, "wasUpdated");
state[indexOfCreateDateColumn] =insertDate;
state[indexOfUpdatedDateColumn] =insertDate;
state[indexOfWasUpdated] ='N';
return true;
}
return false;
}
The second method is for setting the lastUpdatedDate and setting the wasUpdated field to 'Y'.
第二种方法用于设置 lastUpdatedDate 并将 wasUpdated 字段设置为“Y”。
@Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState,
Object[] previousState, String[] propertyNames, Type[] types) {
if (entity instanceof TimeStamped) {
int indexOfLastUpdate = ArrayUtils.indexOf(propertyNames, "lastUpdatedDate");
int indexOfWasUpdated = ArrayUtils.indexOf(propertyNames, "wasUpdated");
currentState[indexOfLastUpdate] = new Date();
currentState[indexOfWasUpdated] ='Y';
return true;
}
return false;
}
HibernateUtil
休眠实用程序
I use this configuration for the session.
我在会话中使用此配置。
public class HibernateUtil {
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;
static {
try {
Configuration configuration = new Configuration().setInterceptor(new TimeStampInterceptor());
configuration.configure();
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
} catch (HibernateException he) {
System.err.println("Error creating Session: " + he);
throw new ExceptionInInitializerError(he);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Versions
版本
I use Maven and Java 1.7.0_40.
我使用 Maven 和 Java 1.7.0_40。
<!--. Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.4.Final</version>
</dependency>
采纳答案by Stefan
Solution:
解决方案:
Thanks to the hint proposed by @vp8106 I could work around the problem. As I set the lastUpdate
value to the same date as the creationDate
value during record initialization, I just compare the two dates. If they are identical, then this is the firstupdate where I need to set the update indicator wasUpdated
to 'Y'
.
感谢@vp8106 提出的提示,我可以解决这个问题。由于我将lastUpdate
值设置为与creationDate
记录初始化期间的值相同的日期,因此我只是比较了两个日期。如果它们是相同的,那么这是我需要设置更新指标firstupdatewasUpdated
来'Y'
。
Code snippet:
代码片段:
@Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState,
Object[] previousState, String[] propertyNames, Type[] types) {
/**
* Update the lastUpdateDate value and set the wasUpdated flag to 'Y'
*/
if (entity instanceof TimeStamped) {
int indexOfLastUpdate = ArrayUtils.indexOf(propertyNames, "lastUpdatedDate");
int indexOfCreatedDate = ArrayUtils.indexOf(propertyNames, "createdDate");
int indexOfWasUpdated = ArrayUtils.indexOf(propertyNames, "wasUpdated");
Date createdDate = (Date) previousState[indexOfCreatedDate];
Date lastUpdateDate = (Date) currentState[indexOfLastUpdate];
/**
* If createdDate equals lastUpdateDate, this is the first update.
* Set the updated column to Y
*/
if (createdDate.equals(lastUpdateDate)) {
logger.warning("This is the first update of the record.");
currentState[indexOfWasUpdated] = 'Y';
}
// set the new date of the update event
currentState[indexOfLastUpdate] = new Date();
return true;
}
return false;
}
回答by vp8106
JavaDoc of onFlushDirty()
method has the following statement:
onFlushDirty()
方法的JavaDoc有如下语句:
Called when an object is detected to be dirty, during a flush.
在刷新期间检测到对象脏时调用。
So there is no difference whether object became dirty due to update()
call or it is dirty due to save()
call. Thus onFlushDirty()
method will be called on every persistent object when session flushing. Session flush may be initiated explicitly by session.flush()
or implicitly in some cases when Hibernate needs it (in your case - before transaction commit).
所以对象update()
是因为save()
调用变脏还是因为调用变脏没有区别。因此onFlushDirty()
,会话刷新时将在每个持久对象上调用方法。session.flush()
在某些情况下,当 Hibernate 需要时(在您的情况下 - 在事务提交之前),会话刷新可能由显式或隐式启动。
In your case wasUpdated
property will allways be saved with 'Y' value: at first onSave()
method will be called, that when session flushes onFlushDirty()
method will be called at the same entity.
在您的情况下,wasUpdated
属性将始终以“Y”值保存:首先onSave()
将调用方法,当会话刷新onFlushDirty()
方法将在同一实体上调用时。
To solve your problem in onFlushDirty()
method you should check if entity was updated. If my memory serves me right, when entity is inserting into the table (saving new), previous state is null. Suggesting to implement onFlushDirty()
like this:
要在onFlushDirty()
方法中解决您的问题,您应该检查实体是否已更新。如果我没记错的话,当实体插入到表中(保存新的)时,之前的状态为空。建议这样实现onFlushDirty()
:
@Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState,
Object[] previousState, String[] propertyNames, Type[] types) {
if (entity instanceof TimeStamped && previousState!=null) {
int indexOfLastUpdate = ArrayUtils.indexOf(propertyNames, "lastUpdatedDate");
int indexOfWasUpdated = ArrayUtils.indexOf(propertyNames, "wasUpdated");
currentState[indexOfLastUpdate] = new Date();
currentState[indexOfWasUpdated] ='Y';
return true;
}
return false;
}
回答by maaartinus
I implemented a similar thing and it works without any problems. There are the following differences:
我实现了一个类似的东西,它没有任何问题。有以下区别:
- I use no
wasUpdated
as comparingcreatedDate
andlastUpdatedDate
suffices. - I use
lastUpdatedDate
for hibernate versioning (millisecond precision is needed for it to work), so I don't have to set it myself.
- 我使用 no
wasUpdated
作为比较createdDate
并且lastUpdatedDate
就足够了。 - 我
lastUpdatedDate
用于休眠版本控制(需要毫秒精度才能工作),所以我不必自己设置。
public boolean onSave(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
if (entity instanceof TimeStamped) {
Date insertDate = new Date();
int indexOfCreateDateColumn = ArrayUtils.indexOf(propertyNames, "createdDate");
state[indexOfCreateDateColumn] = insertDate;
return true;
}
return false;
}
If I were the OP, I'd ask myself if I really need wasUpdated
as a field. It's clearly redundant as it can be computed anytime the way the OP did in his answer. A read-only property on TimeStamped
should do.
如果我是 OP,我会问自己是否真的需要wasUpdated
作为一个领域。这显然是多余的,因为它可以像 OP 在他的回答中所做的那样随时计算。TimeStamped
应该做一个只读属性。