当前位置: 首页 > 知识库问答 >
问题:

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

湛嘉歆
2023-03-14

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

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

我想解释一下,当我创建并持久化一个新对象时,createDate和lastUpdate字段具有相同的日期,但是wasUpdate字段被设置为“N”,因为没有更新。我只使用会话。save()在我的代码中,没有会话。update()并且也没有会话。saveOrUpdate()。Hibernate的日志表明实际上有一个更新,它将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设置为'Y',并将wasUpdate字段设置为'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;
}

我在会话中使用此配置。

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和Java1.7。0_40。

    <!--. Hibernate -->
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>4.3.4.Final</version>
    </dependency>

共有3个答案

田宇
2023-03-14

我实现了一个类似的东西,它工作起来没有任何问题。有以下区别:

  • 我使用nowasdupdated来比较createdDatelastdupdateDate就足够了
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,我会问自己是否真的需要wasdupdated作为一个字段。这显然是多余的,因为它可以在任何时候计算,就像OP在他的回答中所做的那样。时间戳上的只读属性应该可以。

华峰
2023-03-14

多亏@vp8106的提示,我可以解决这个问题。当我在记录初始化期间将lastUpdate值设置为与creationDate值相同的日期时,我只是比较这两个日期。如果它们相同,那么这是第一次更新,我需要将更新指示符wasdupdated设置为'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;
}
穆鸿飞
2023-03-14

onFlushDirty()方法的JavaDoc具有以下语句:

在冲洗过程中检测到对象变脏时调用

因此,对象是由于调用date()而变脏,还是由于调用保存()而变脏,没有区别。因此,当会话刷新时,将对每个持久对象调用onFlushDirty()方法。会话刷新可以由session.flush()显式启动,也可以在Hibernate需要时(在您的情况下-在事务提交之前)隐式启动。

在您的情况下,wasdupdated属性将始终使用“Y”值保存:首先将调用onSave()方法,当会话刷新时,将在同一实体上调用onFlushDirty()方法

要解决onFlushDirty()方法中的问题,您应该检查实体是否已更新。如果我没记错的话,当实体插入到表中(保存new)时,以前的状态是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;
}
 类似资料:
  • 我的应用程序由spring@transaction和hibernate组成。我正在尝试使用hibernate拦截器。我把拦截器注册为 实际的拦截器扩展了EmptyInterceptor,我正在重写onSave()方法。 问题是调用了interceptor onSave()方法,但在此之后,实际实体不会插入到数据库中。

  • 问题内容: 我尝试了一些来自hibernate 4.0拦截器的 代码,该代码为使用会话级拦截器提供了以下代码: 但是,我同时检查了hibernate-core 4.0源代码和onliehibernate 4.0 java- doc ,该类没有方法,但是hibernate 3.6 java- doc确实具有此方法。 有人知道该方法移到哪里吗?如果已弃用,为什么文档仍将其保留在教程文档中?在4.0中如

  • 假设我有两个类Foo和Bar。Foo有一个多对一的关联到Bar,映射如下: 我已经创建了一个自定义拦截器,该拦截器从EmptyInterceptor扩展并覆盖onSave方法,如下所示: 问题在于,尽管对bar执行SQL insert语句,但对实体对象执行SQL update语句,实体对象是一个Foo,在插入之前已被修改。这将导致以下Hibernate异常。 最后一个原因:批量更新从更新[0]返回

  • 问题内容: 我想在Java-SE应用程序中使用拦截器,并且将weld作为CDI实现,并且在这里进行测试: 主班: 服务等级: 拦截器类: Aaa和输出: 我的问题 第一:为什么我在调用methodCallNumberTwo()时没有在methodCall()中调用拦截器? 第二:有办法改变吗? 我仅研究拦截器的行为,并且想了解。先感谢您! 问题答案: 不会调用拦截器,因为您是在对象的同一实例上调用

  • 问题内容: 我正在使用Java EE 6和Jboss AS7.1,并尝试使用拦截器绑定(来自jboss网站的示例)。 我有一个InterceptorBinding注解: 拦截器: 还有一个豆: 但是拦截器没有被称为。。。 在编写此代码时将调用拦截器: 谢谢你的帮助。 问题答案: 您是否按照参考示例中的说明启用了拦截器? 缺省情况下,bean档案没有通过拦截器绑定绑定的已启用拦截器。必须通过将侦听器

  • 问题内容: 我创建了一个RestEASY拦截器,以允许我在Web服务调用完成后在HTTP响应上设置标头值。我的代码看起来像这样… 但是,当我调用服务时,永远不会调用拦截器。我看到webservice调用成功完成,但是拦截器中的任何代码都没有执行过。除了注册拦截器,我还需要做些其他事情吗?是否必须在其他任何地方声明?是否需要包含任何特殊的web.xml参数? 问题答案: 您必须在web.xml的re