【问题标题】:Inconsistencies when executing Hibernate update执行 Hibernate 更新时的不一致
【发布时间】:2016-11-24 18:15:25
【问题描述】:

我有一个 MQ 侦听器,它侦听消息并将状态更新到 DB。我有一个设置,其中 Hibernate 会话由 Spring 管理。

下面是MQ监听配置。

        <bean id="queue" class="com.ibm.mq.jms.MQQueue">
             <constructor-arg value="queuename" />
        </bean>
        <bean id="listenerBean" class="com.mypackage.Listener">
                <property name="service" ref="myService" />
        </bean>
        <bean id="listener" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
                <constructor-arg><ref bean="listenerBean"/></constructor-arg>
        </bean>
        <bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
                <property name="connectionFactory" ref="connectionFactory" />
                <property name="destination" ref="queue" />
                <property name="messageListener" ref="listener" />
                <property name="exceptionListener" ref="exceptionListener" />
                <property name="sessionTransacted" value="true" />
                <property name="autoStartup" value="true" />
        </bean>

在Java端处理MQ消息,

public class Listener implements MessageDelegate{

        public MyService service;

        @Override
        public void handleMessage(Serializable message) {
                service.process(message);
        }
}

服务类中的 process 方法调用 DAO 方法来更新 DB。

以下是用于 Spring sessionFactory bean org.springframework.orm.hibernate3.LocalSessionFactoryBean 的休眠属性。

<property name="hibernateProperties">
          <props>
                   <prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
                    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
                    <prop key="hibernate.cache.use_second_level_cache">true</prop>
                    <prop key="hibernate.cache.use_query_cache">true</prop>
                    <prop key="hibernate.show_sql">false</prop>
                    <prop key="hibernate.generate_statistics">true</prop>
                    <prop key="hibernate.cache.provider_configuration_file_resource_path">ehcache_db_custom.xml</prop>
          </props>
    </property>

这个 sessionFactory 被注入到我的 DAO bean 中,我使用 sessionFactory.getCurrentSession() 获取我的会话

使用这个session,我正在使用Criteria API 根据我从MQ 消息中获得的唯一值获取bean,对于我获得的唯一结果,我将状态更改为成功并再次调用sessionFactory.getCurrentSession() 到获取session 并调用session.update() 来更新数据库。

bean 上没有注释,因为我使用的是 hbm 文件。它是一个简单的 bean,没有一对多或多对一的映射。没有@Transactional 注释。

下面是更新sn-p。我相信 Spring 会处理事务。

    Session session = sessionFactory.getCurrentSession();
    if(session != null && bean != null) {
       session.update(bean);
    }

我有一个记录器(由我添加,log4j),用于在数据库更新失败时成功更新数据库并处理异常。

奇怪的部分来了。

我在多线程环境中遇到过一个场景,其中日志不显示休眠引发的任何异常,并且日志显示数据库已成功更新,但是状态未更新。这只发生在某些记录上,而不是全部。有成功更新的记录。我找不到任何特定于数据的问题。

当我执行使用 JUnit 更新失败的记录之一时,它更新成功。

如果我在配置结束时遗漏了什么,有人可以告诉我吗?

【问题讨论】:

  • 您能添加您的 MQ 侦听器代码并告诉我们配置了多少侦听器吗?
  • @javaguy 更新了问题中的 MQ 侦听器代码。分布在 2 个服务器上的 8 个 JVM 上有 8 个监听器。

标签: java spring hibernate session


【解决方案1】:

您的应用程序中存在设计问题,即基本上,在多线程 JMS 应用程序中,当消费者使用多个线程进行侦听时,消息将失去顺序(生产者产生的顺序)并且您会发现不正确的更新如果您不仔细设计应用程序,则在数据库中(无一例外,我们在应用程序中曾遇到过同样的问题)。

所以要解决这个问题,一种方法是你需要从数据库端有一个业务逻辑来检查要更新的记录是否真的是正确的(你需要在单个事务中使用适当的锁定来处理这个)。例如,如果您的应用程序在products 数据库表中保存number_of_products(按递增顺序),那么您需要检查是否只有当新消息出现更高价值的产品时才应该进行新的更新.

另一个解决方案是,创建一个像适配器这样的入口层(应该是单线程的)并使用一些业务逻辑生成一个序列号(比如每个product_type 都会有一个序列)并在运行期间检查该序列号数据库更新。如果序列号低于前一个值,则应忽略该消息,因为它不是最新消息,即最新消息已更新到数据库。

【讨论】:

  • 基本上,从 MQ 消息接收到的唯一 Id 是进程的一部分。这条记录在 1 或 2 天前被插入到 DB 中,状态为New,所以它肯定应该存在于 DB 中。我不介意MQ消息的消费顺序,只要相应的记录更新为Completed
  • 除了 New 和 Completed 之间的其他状态是什么?
  • In Progress,但这是特定于应用程序的。基本上,更改为New 后有一些过程,然后更新为In Progress,然后有一个延迟。然后应用程序从 MQ 获取消息以将状态更新为Completed。我看到一些状态为In Progress 的记录,即使已从 MQ 收到消息。
  • 所以问题是当“某个进程”在消息更新后更新记录时,它会再次将“已完成”覆盖为“进行中”。
  • 没有代码可以将其从 Completed 更新回 In Progress。仅来自New 的记录更改为In Progress
猜你喜欢
  • 1970-01-01
  • 2022-11-24
  • 2014-11-07
  • 2019-03-11
  • 2011-07-03
  • 1970-01-01
  • 1970-01-01
  • 2014-02-20
  • 2020-10-18
相关资源
最近更新 更多