в моем приложении У меня проблема с блокировкой и не знаю, смогу ли я ее обработать. Я использую JPA 2.1 (с eclipselink) на сервере из морской рыбы.Взаимозаполнение JPA с @TransactionAttribute (TransactionAttributeType.REQUIRES_NEW)
Есть два EJB. OuterBean
должен написать LogEntry
в базу данных и звонить InnerBean
в цикле. InnerBean
сам должен написать LogEntry
и сделать некоторые другие вещи (больше манипуляций с базами данных на других объектах). Вызовы InnerBean#execute()
независимы друг от друга, что означает, что если один метод не работает (отката), остальные должны продолжать работать. Поэтому InnerBean#execute()
работает в своей собственной транзакции.
При выполнении кода ниже я получаю java.sql.SQLException: Lock wait timeout exeeded; try restarting transaction
под MySQL и java.sql.SQLSyntaxErrorException: ORA-02049: timeout: distributed transaction waiting for lock
под Oracle (Postgres просто ждет вечность, вероятно, из-за плохой сконфигурированной базы данных).
Я не эксперт по базе данных/JPA, но я думаю, проблема в том, что две транзакции хотят писать в одном и том же формате данных. То, что я не делаю , понимает, что существует проблема вообще, потому что эти манипуляции с базами данных являются вставками, которые должны быть независимыми от других . Есть ли способ, которым я могу реализовать этот вариант использования (нужно ли использовать транзакции, управляемые bean-обработкой, есть ли какие-либо аннотации, которые я могу использовать, могу ли принудительно совершить фиксацию в OuterBean#execute()
после em.persist (logEntry), а до цикла так что блокировки транзакций выпущены в любом случае)?
@Stateless
public class OuterBean
{
@PersistenceContext(unitName = "PU_LOGGER")
private EntityManager em;
@EJB
private InnerBean innerBean;
public void execute()
{
LogEntry logEntry = new LogEntry();
logEntry.setDate(new Date());
logEntry.setMessage("OuterBean#execute()");
em.persist(logEntry);
for(int i = 0; i < 10; ++i)
{
innerBean.execute();
}
}
}
@Stateless
public class InnerBean
{
@PersistenceContext(unitName = "PU_LOGGER")
private EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void execute()
{
LogEntry logEntry = new LogEntry();
logEntry.setDate(new Date());
logEntry.setMessage("InnerBean#execute()" + Math.random());
em.persist(logEntry);
}
}
EDIT
Вот журнал, который генерируется
FINER: client acquired: 1990452734
FINER: TX binding to tx mgr, status=STATUS_ACTIVE
FINER: acquire unit of work: 38847372
FINEST: persist() operation called on: LogEntry{id=null, message=OuterBean#execute(), date=Mon Jul 18 09:00:27 CEST 2016}.
FINER: TX beginTransaction, status=STATUS_ACTIVE
FINEST: Connection acquired from connection pool [default].
FINEST: Execute query DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + #PREALLOC_SIZE WHERE SEQ_NAME = #SEQ_NAME")
FINEST: reconnecting to external connection pool
FINE: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [2 parameters bound]
FINEST: Execute query ValueReadQuery(name="SEQUENCE" sql="SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = #SEQ_NAME")
FINE: SELECT SEQ_COUNT FROM SEQUENCE WHERE SEQ_NAME = ?
bind => [1 parameter bound]
FINEST: local sequencing preallocation for SEQ_GEN: objects: 50 , first: 301, last: 350
FINEST: assign sequence to the object (301 -> LogEntry{id=null, message=OuterBean#execute(), date=Mon Jul 18 09:00:27 CEST 2016})
FINER: client acquired: 187184807
FINER: TX binding to tx mgr, status=STATUS_ACTIVE
FINER: acquire unit of work: 2098992041
FINEST: persist() operation called on: LogEntry{id=null, message=InnerBean#execute()0.3957184758563761, date=Mon Jul 18 09:00:28 CEST 2016}.
FINER: TX beginTransaction, status=STATUS_ACTIVE
FINEST: Connection acquired from connection pool [default].
FINEST: Execute query DataModifyQuery(name="SEQUENCE" sql="UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?")
FINEST: reconnecting to external connection pool
FINE: UPDATE SEQUENCE SET SEQ_COUNT = SEQ_COUNT + ? WHERE SEQ_NAME = ?
bind => [2 parameters bound]
Какие блокировки вы делаете в приложении или на объекте? Вы проверили, что ваш контейнер фактически запускает новую транзакцию, или аннотацию можно просто игнорировать, а метод завершен в более крупном транзакционном контексте? Включите ведение журнала до лучших и посмотрите, что происходит, поскольку на самом деле этого недостаточно. – Chris
Привет @Chris, я добавил созданный FINEST журнал. Я все не понимаю, но вижу, что две транзакции будут запущены. Это соответствовало бы моему наблюдению, что если я оставлю аннотацию '@TransactionAttribute (REQUIRES_NEW)' inInnerBean', все будет хорошо. Когда дело доходит до блокировки, я не делаю явной блокировки. Вышеупомянутый код будет выполнен через CDI bean, и больше нет кода, где я бы заблокировал что-либо. – Filou
Похоже, вам нужно настроить пул соединений для последовательности, чтобы он мог получать/распределять порядковые номера из вашей таблицы последовательностей вне транзакции JTA. см. http://www.eclipse.org/eclipselink/documentation/2.4/concepts/data_access006.htm#CHDEFJHH и http://stackoverflow.com/questions/19732551/how-to-configure-an-eclipselink-jta-sequence -connection-pool, который показывает, как указать источник данных, не относящийся к JTA – Chris