我在一个在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();
}
答案 0 :(得分:0)
我想弄清楚整个过程,所以我在下面报道的陈述可能是基于错误的图片。
如果我理解正确,即使网络服务花了10毫秒,你也会遇到问题。无论如何,请求都可以在中间。
但是,我说你创建实体管理器而不是通过容器注入我是错误的吗?如果在所有单例方法中共享同一个管理器,则可以更好地控制缓存并发访问。
其次,如果对Web服务的调用对于最终值不是强制性的,那么您是否有特殊原因要求异步调用Web服务,使用消息驱动Bean或使用@Asynchronous
注释?
[UPDATE] 您不能拥有依赖10秒WS响应的实时并发访问应用程序。
例如,如果最后一步WS调用失败,您会怎么做?回滚?那么,如果新传入的客户端读取了您以后要回滚的未提交数据,该怎么办?
正如其他人可能已经说过的那样,使最后一次WS调用异步,或者更好地委托给消息驱动的bean(它具有在发生故障时重试WS调用的优点)。无论哪种方式,呼叫都会立即返回给客户端。
但我相信你会再次遇到同样的问题。如果我正确理解了架构,我会重新考虑设计来做这件事: