#java #hibernate #jpa #ejb
#java #спящий режим #jpa #ejb
Вопрос:
Я работаю над сложным веб-приложением, которое использует EJB и Hibernate в JBoss. Я использую одноэлементный EntityManagerFactory и разделяю его между всеми запущенными процессами, используя экземпляр Entity Manager.
Проблема возникает, когда в действии структуры вызывается обновление объекта, и перед завершением действия другой процесс считывает и обновляет тот же объект. У меня есть этот второй процесс (это веб-служба, вызываемая из третьих частей), считывающий старое значение, а не обновленное в действии.
Я знаю, что данные становятся постоянными в базе данных только после завершения действия и возврата управления пользователю. К сожалению, это действие после выполнения слияния Entity Manager должно вызывать веб-службу, которая иногда возвращается через 10 секунд. Между тем, другой процесс имеет неверное значение, если прочитал этот объект.
Мне нужно, чтобы слияние в первом процессе стало мгновенно постоянным, или мне нужно, чтобы другой процесс прочитал правильное значение. Я не знаю, работает ли кэш второго уровня и действует ли он в этом сценарии.
Решение состоит в том, чтобы выполнить обновление с использованием 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>
это пример обновления Entity Manager
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. Я не знаю точно, как настроено ваше приложение, но вы, вероятно, сможете увидеть изменения, внесенные в БД после возврата метода EJB (если он выполняется при транзакции CMT). Возможно, вызов WS (тот, который длится до 10 секунд) выполняется из метода EJB, и именно это задерживает фиксацию транзакции? Если да, возможно ли (с помощью логики приложения) реорганизовать код так, чтобы вызов WS выполнялся вне метода EJB?
2. Сбросьте в T1 и установите T2 для уровня изоляции транзакции на READ_UNCOMMITTED
3. Решение Алана Хэя должно быть самым простым. Другой подход заключается в том, чтобы попытаться каким-то образом убедиться, что удаленный запрос, который поступает в середине, использует тот же объект EntityManager. Это, однако, совсем другая проблема, когда нужно определить, какой из них и убедиться, что вы не закрываете его слишком рано, но вы все равно закрываете его.
4. @JohnDonn да, вызов веб-службы выполняется в действии struct, в этом действии у меня есть вызов метода ejb, который использует Entity Manager для чтения / записи значений из базы данных, а затем вызов веб-службы, который иногда может возвращаться через несколько секунд. Только после завершения действия и возврата элемента управления пользователю я могу видеть обновленные данные в базе данных. Между тем другой процесс считывает устаревшие данные, а не тот, который обновляется из вызова ejb.
5. «Я знаю, что данные становятся постоянными в базе данных только после того, как действие завершит свою работу, а управление вернется к пользователю». — это кажется мне странной частью. Вы действительно отладили код и увидели, что после возврата метода EJB (перед длинным вызовом WS) внешний клиент MySQL показывает, что данные не были изменены? Если это так, поскольку мне кажется странным, что транзакция фиксируется при возврате Action.execute() , возможно, именно после длительного вызова WS вы видите изменения? И в таком случае, что делает этот длинный вызов?
Ответ №1:
Я пытаюсь разобраться во всем процессе, поэтому приведенные ниже утверждения, вероятно, основаны на неправильной картине.
Если я правильно понял, у вас возникнут проблемы, даже если веб-службе потребуется 10 мс. В любом случае запрос может прийти посередине.
Но, я ошибаюсь, говоря, что вы создаете диспетчер сущностей, а не внедряете контейнер? Если вы используете один и тот же менеджер во всех ваших одноэлементных методах, вы можете иметь больший контроль над доступом к параллелизму кэша.
Во-вторых, если вызов веб-службы не является обязательным для конечного значения, есть ли особая причина, по которой вы не вызываете веб-службу асинхронно, с помощью компонента, управляемого сообщениями, или с помощью @Asynchronous
аннотации?
[ОБНОВЛЕНИЕ] У вас не может быть приложения с одновременным доступом в реальном времени, которое полагается на 10-секундный ответ WS.
Например, если на последнем шаге вызов WS завершается неудачно, что вы делаете? Откат? Итак, что, если новый входящий клиент прочитал незафиксированные данные, которые вы позже собираетесь откатить?
Как, вероятно, сказали некоторые другие, сделайте последний вызов WS асинхронным или лучше делегируйте его компоненту, управляемому сообщениями (преимущество которого заключается в повторении вызова WS в случае сбоя). В любом случае вызов немедленно возвращается клиенту.
Но я совершенно уверен, что у вас снова возникнет та же проблема. Если бы я правильно понял архитектуру, я бы пересмотрел дизайн, чтобы сделать это:
- Вызывайте WS асинхронно или с MDB, чтобы вернуть вызов клиенту
- Для обеспечения потокобезопасности выполняйте вызовы диспетчеру сущностей, по крайней мере, тому, который настаивает на той же таблице. Должно быть легко, потому что у вас одноэлементный класс
Комментарии:
1. служба заняла 10 секунд, а не 10 мс, и мне нужен ответ, чтобы продолжить работу с бизнес-логикой. У меня есть одноэлементный класс для выделения EntityManagerFactory, а затем совместного использования этого экземпляра между всеми процессами, которым необходимо выполнять чтение / запись из базы данных.