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

Ste*_*fan 9 java state hibernate updates interceptor

计划

我正在使用Hibernate为一个小项目实现createDate和lastUpdate时间戳.我使用一个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>
Run Code Online (Sandbox Code Playgroud)

这是我创建对象的方式:

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();
Run Code Online (Sandbox Code Playgroud)

拦截器

@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;
    }
Run Code Online (Sandbox Code Playgroud)

第二种方法是设置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;
}
Run Code Online (Sandbox Code Playgroud)

的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;
    }
}
Run Code Online (Sandbox Code Playgroud)

版本

我使用Maven和Java 1.7.0_40.

    <!--. Hibernate -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.3.4.Final</version>
    </dependency>
Run Code Online (Sandbox Code Playgroud)

小智 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;
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*fan 3

解决方案:

感谢@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;
}
Run Code Online (Sandbox Code Playgroud)