在Hibernate 3和struts

时间:2016-09-29 13:32:20

标签: java hibernate jpa ejb

我在一个在JBoss上使用EJB和Hibernate的复杂Web应用程序上工作。我使用单例EntityManagerFactory,并使用Entity Manager实例在所有正在运行的进程之间共享它。

当在struct动作中调用实体更新时,并且在操作结束之前,另一个进程读取并更新同一对象时,会出现问题。 我有第二个进程(它是从第三部分调用的Web服务)读取旧值而不是操作中更新的值。

我知道只有在操作结束后,数据才会在数据库上持久保存,并且控制权将返回给用户。不幸的是,在Entity Manager合并执行后,它必须调用有时在10秒后返回的Web服务。同时,如果读取此对象,其他进程的值就会错误。

我需要在第一个进程中合并立即持久化,或者,我需要其他进程读取正确的值。 我不知道二级缓存是否正常工作并且在这种情况下有效。

解决方案是使用JDBC而不是Hibernate进行更新,但我想要一个干净的解决方案来实现。

简要概述

t0 = start action   ;t1= action find and merge entity; t2= start call to web service; t6= web service return; tend = end action  ; 
t3= start second process ; t4= find and merge entity; t5=end second process

t0             t1        t2    t3    t4  t5            t6        tend

|---------------|--------|------------------------------|-------|

                               |-----|----|

我需要在t3读取的值是在t2合并的值。

这是我的persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="ApplicationWeb_EJB" transaction-type="JTA">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <jta-data-source>java:/ds/VisiaIntegrazioneDS</jta-data-source>
    <class>.....entity.ApplicationServer</class>
    ....
    <class>.....entity.Devices</class>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect"/>
      <!-- Caching properties -->
      <property name="hibernate.cache.use_second_level_cache" value="true"/>
      <!--<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />-->
      <property name="net.sf.ehcache.configurationResourceName" value="ehcache.xml"/>
      <!--<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>-->
      <!--<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />-->
      <property name="hibernate.cache.use_query_cache" value="true"/>
      <property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory"/>
      <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
      <property name="hibernate.max_fetch_depth" value="4"/>
    <!-- hibernate.generate_statistics a true produce informazioni su hibernate da loggare -->
      <property name="hibernate.generate_statistics" value="true"/> 

    </properties>
  </persistence-unit>
</persistence>

这是实体管理器更新的示例

EntityManager em = EntityMan.getEMF().createEntityManager();

        try {

            em.find(Devices.class, device.getId());
            em.merge(device);
            em.flush();
        } catch (Exception e) {
            logger.debug(e.getMessage());
            e.printStackTrace();
        }

1 个答案:

答案 0 :(得分:0)

我想弄清楚整个过程,所以我在下面报道的陈述可能是基于错误的图片。

如果我理解正确,即使网络服务花了10毫秒,你也会遇到问题。无论如何,请求都可以在中间。

但是,我说你创建实体管理器而不是通过容器注入我是错误的吗?如果在所有单例方法中共享同一个管理器,则可以更好地控制缓存并发访问。

其次,如果对Web服务的调用对于最终值不是强制性的,那么您是否有特殊原因要求异步调用Web服务,使用消息驱动Bean或使用@Asynchronous注释?

[UPDATE] 您不能拥有依赖10秒WS响应的实时并发访问应用程序。

例如,如果最后一步WS调用失败,您会怎么做?回滚?那么,如果新传入的客户端读取了您以后要回滚的未提交数据,该怎么办?

正如其他人可能已经说过的那样,使最后一次WS调用异步,或者更好地委托给消息驱动的bean(它具有在发生故障时重试WS调用的优点)。无论哪种方式,呼叫都会立即返回给客户端。

但我相信你会再次遇到同样的问题。如果我正确理解了架构,我会重新考虑设计来做这件事:

  1. 异步调用WS或使用MDB调用WS以便返回对客户端的调用
  2. 对实体管理器(至少是坚持同一个表的实体管理器)的调用是线程安全的。应该很容易,因为你有一个单例类