计划
我正在使用Hibernate为一个小项目实现createDate和lastUpdate Timestamps。我使用EmptyInterceptor,并根据我在这里找到的建议解决方案重载所提供的方法。除非有一点细节,否则解决方案效果良好。我想添加一列,指示对象是否已经更新。我知道我可以通过简单地比较创建和更新的两个时间戳是否有差异来实现这一点,但我需要有这个字段来指示有更新。
我使用onSave方法,该方法在存储新对象时被调用,将wasUpdated值设置为"N",表示没有更新。在onFlushDirty((方法中,我将该值设置为"Y"。
问题
我想假设,当我创建并持久化一个新的Object时,createDate和lastUpdate字段具有相同的Date,但wasUpdated字段被设置为"N",因为没有更新。我在代码中只使用session.save((,没有session.update((也没有session.saveOrUpdate((。Hibernate的日志表明实际上有一个update,它将wasUpdated值设置为"Y"。
此更新的来源是什么?它是在哪里触发的
对象初始化和持久性
我在hibernate.cfg.xml.中禁用了自动提交
<property name="hibernate.connection.autocommit">false</property>
这就是我创建对象的方式:
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;
}
第二种方法是设置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;
}
休眠Util
我将此配置用于会话。
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;
}
}
版本
我使用Maven和Java 1.7.0_40。
<!--. Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.4.Final</version>
</dependency>
onFlushDirty()
方法的JavaDoc有以下语句:
当在刷新过程中检测到对象脏时调用。
因此,对象是由于update()
调用而变脏,还是由于save()
调用而变污,并没有区别。因此,会话刷新时,每个持久对象都会调用onFlushDirty()
方法。会话刷新可以由session.flush()
显式启动,或者在某些情况下,当Hibernate需要时(在您的情况下,在事务提交之前(隐式启动
在您的情况下,wasUpdated
属性将始终使用"Y"值保存:首先将调用onSave()
方法,即当会话刷新时,将在同一实体调用onFlushDirty()
方法
为了解决onFlushDirty()
方法中的问题,您应该检查实体是否已更新。如果我的内存正常,当实体插入到表中(保存新的(时,以前的状态为null。建议像这样实现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;
}
解决方案:
感谢@vp8106提出的提示,我可以解决这个问题。当我在记录初始化期间将lastUpdate
值设置为与creationDate
值相同的日期时,我只是比较这两个日期。如果它们相同,那么这是我需要将更新指示符wasUpdated
设置为'Y'
的第一次更新。
代码段:
@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;
}
我实现了类似的东西,它工作起来没有任何问题。有以下区别:
- 我不使用CCD_ 15,因为比较CCD_ 16和CCD_
- 我使用
lastUpdatedDate
进行hibernate版本控制(需要毫秒精度才能工作(,所以我不必自己设置
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;
}
如果我是OP,我会问自己是否真的需要wasUpdated
作为一个领域。这显然是多余的,因为它可以像OP在回答中那样随时计算。TimeStamped
上的只读属性应该可以。