Предотвращение отката транзакций в JBoss + Hibernate
у нас есть приложение Java, работающее на JBoss 5.1, и в некоторых случаях нам нужно предотвратить закрытие транзакции в случае JDBCException
выбрасывается каким-то базовым методом.
у нас есть метод EJB, который выглядит следующим образом
@PersistenceContext(unitName = "bar")
public EntityManager em;
public Object foo() {
try {
insert(stuff);
return stuff;
} (catch PersistenceException p) {
Object t = load(id);
if (t != null) {
find(t);
return t;
}
}
}
если insert
не удается из-за PersistenceException
(который обертывает JDBCException
вызвано нарушением ограничения), мы хотим продолжить выполнение с load
в рамках той же транзакции.
мы не в состоянии сделать это прямо сейчас, потому что транзакция закрыта контейнером. Вот что мы видим в журналах:
org.hibernate.exception.GenericJDBCException: Cannot open connection
javax.persistence.PersistenceException: org.hibernate.exception.GenericJDBCException: Cannot open connection
at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
...
Caused by: javax.resource.ResourceException: Transaction is not active: tx=TransactionImple < ac, BasicAction: 7f000101:85fe:4f04679d:182 status: ActionStatus.ABORT_ONLY >
класс EJB отмечен следующими примечаниями
@Stateless
@TransactionManagement(TransactionManagementType.CONTAINER)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
есть ли какой-либо правильный способ предотвратить откат транзакции только в этом конкретном случае?
2 ответов
вы действительно не должны пытаться сделать это. Как упоминалось в другом ответе и цитировании документов Hibernate, никакое исключение, вызванное Hibernate, не должно рассматриваться как восстанавливаемое. Это может привести вас к некоторым трудным для поиска/отладки проблемам, особенно с автоматической грязной проверкой hibernate.
чистый способ решить эту проблему-проверить эти ограничения перед вставкой объекта. Используйте запрос для проверки, нарушается ли ограничение базы данных.
public Object foo() {
if (!objectExists()) {
insertStuff();
return stuff();
}
// Code for loading object...
}
I знаю, это кажется немного болезненным, но это единственный способ узнать, какое ограничение было нарушено (вы не можете получить эту информацию из исключений Hibernate). Я считаю, что это самое чистое решение (самое безопасное, по крайней мере).
Если вы все еще хотите восстановить из исключения, вам придется внести некоторые изменения в свой код.
Как уже упоминалось, вы можете управлять транзакциями вручную, но я не рекомендую этого. API JTA действительно громоздкий. Кроме того, если вы используете Bean Managed Transaction (BMT), вам придется вручную создавать транзакции для каждого метода в вашем EJB, это все или ничего.
С другой стороны, вы могли бы преобразовать свои методы, поэтому контейнер будет использовать различные транзакции для запроса. Что-то вроде этого:--3-->
@Stateless
public class Foo {
...
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public Object foo() {
try {
entityManager.insert(stuff);
return stuff;
} catch (PersistenceException e) {
if (e.getCause() instanceof ConstraintViolationException) {
// At this point the transaction has been rolled-backed.
// Return null or use some other way to indicate a constrain
// violation
return null;
}
throw e;
}
}
// Method extracted from foo() for loading the object.
public Object load() {
...
}
}
// On another EJB
@EJB
private Foo fooBean;
public Object doSomething() {
Object foo = fooBean.insert();
if (foo == null) {
return fooBean.load();
}
return foo;
}
при вызове foo () текущая транзакция (T1) будет приостановлена, и контейнер создаст новую (T2). При возникновении ошибки T2 будет откатываться, и T1 будет восстановлен. Когда load () вызывается, он будет использовать T1 (который все еще активен).
надеюсь, что это помогает!
Я не думаю, что это возможно.
In может зависеть от вашего поставщика JPA, но, например, Hibernate явно заявляет, что любое исключение оставляет сеанс в несогласованном состоянии и, следовательно, не должно рассматриваться как восстанавливаемое (13.2.3. Обработка исключений).
Я думаю, лучшее, что вы можете сделать, это отключить автоматическое управление транзакциями для этого метода и создать новую транзакцию после исключения вручную (с помощью UserTransaction
, насколько я помнить.)