Hibernate拦截器 - 为什么在onSave之后调用onFlushDirty?

时间:2014-08-06 08:43:10

标签: java hibernate state updates interceptor

计划

我正在使用Hibernate为一个小项目实现createDate和lastUpdate时间戳。我使用EmptyInterceptor并根据我找到的here建议的解决方案重载提供的方法。 解决方案除了一个小细节外,工作正常。我想添加一个列,指示对象是否已经更新。我知道我可以通过简单比较两个创建和更新的时间戳是否存在差异来实现这一点,但我需要让此字段指示存在更新。

我使用onSave方法,当存储新对象时将其调用以将wasUpdated值设置为“N”,表示没有更新。在onFlushDirty()方法中,我将此值设置为“Y”。

问题

我想说当我创建并持久化新对象时,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();

拦截器

@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;
}

的HibernateUtil

我将此配置用于会话。

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>

3 个答案:

答案 0 :(得分:5)

onFlushDirty()方法的JavaDoc具有以下声明:

  

在冲洗期间检测到物体变脏时调用。

因此,update()调用对象变脏是否变脏或save()呼叫导致对象变脏是没有区别的。因此,当会话刷新时,将在每个持久对象上调用onFlushDirty()方法。会话刷新可以由session.flush()显式启动,或者在Hibernate需要它时(在您的情况下 - 在事务提交之前)隐式启动。

在你的情况下,wasUpdated属性将始终以“Y”值保存:将调用第一个onSave()方法,当会话刷新onFlushDirty()方法将被调用时实体。

要在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;
}

答案 1 :(得分:1)

解决方案:

感谢@ 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;
}

答案 2 :(得分:0)

我实现了类似的东西,它没有任何问题。有以下不同之处:

  • 我不使用wasUpdated作为比较createdDatelastUpdatedDate足够的。
  • 我使用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上的只读属性应该这样做。