gpt4 book ai didi

java - 在 JBoss + Hibernate 中防止事务回滚

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:23:42 24 4
gpt4 key购买 nike

我们有一个在 JBoss 5.1 上运行的 Java 应用程序,在某些情况下,我们需要防止事务关闭,以防某些底层方法抛出 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)

是否有任何适当的方法来防止事务在这种特定情况下回滚?

最佳答案

你真的不应该尝试那样做。正如在另一个答案中提到的,并引用了 Hibernate Docs,Hibernate 抛出的任何异常都不应被视为可恢复的。这可能会导致您遇到一些难以发现/调试的问题,特别是 hibernate 自动脏检查。

解决此问题的一种简洁方法是在插入对象之前检查这些约束。使用查询检查是否违反了数据库约束。

public Object foo() {
if (!objectExists()) {
insertStuff();
return stuff();
}
// Code for loading object...
}

我知道这看起来有点痛苦,但这是您确定违反了哪个约束的唯一方法(您无法从 Hibernate 异常中获取该信息)。我相信这是最干净的解决方案(至少是最安全的)。


如果您仍想从异常中恢复,则必须对代码进行一些修改。

如前所述,您可以手动管理交易,但我不建议这样做。 JTA API 实在是太麻烦了。此外,如果您使用 Bean Managed Transaction (BMT),则必须为 EJB 中的每个方法手动创建事务,要么全有要么全无。

另一方面,您可以重构您的方法,以便容器为您的查询使用不同的事务。像这样:

@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(它仍然处于 Activity 状态)。

希望对您有所帮助!

关于java - 在 JBoss + Hibernate 中防止事务回滚,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8730617/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com