我有一个方法“databaseChanges”,它以迭代方式调用 2 个操作:A、B。 “A”在前,“B”在后。 “ A ”和“ B ”可以在我的持久存储 Oracle 数据库 11g 中创建、更新和删除功能。
比方说,
‘A’ 更新表 Users 中的记录,属性 zip,其中 id = 1。
‘B’ 在表 hobbies 中插入一条记录。
场景: 调用了databaseChanges方法,’A’操作并更新记录。 ‘B’ 操作并尝试插入一条记录,发生了一些事情,抛出异常,异常冒泡到 databaseChanges 方法。
预期: “A”和“B”没有任何改变。 “A”所做的更新将被回滚。 ‘B’ 没有改变任何东西,好吧……有一个例外。
实际: “A”更新似乎没有被回滚。 ‘B’ 没有改变任何东西,好吧……有一个例外。
一些代码
如果我有连接,我会做类似的事情:
private void databaseChanges(Connection conn) {
try {
conn.setAutoCommit(false);
A(); //update.
B(); //insert
conn.commit();
} catch (Exception e) {
try {
conn.rollback();
} catch (Exception ei) {
//logs...
}
} finally {
conn.setAutoCommit(true);
}
}
问题: 我没有连接(请参阅随问题发布的标签)
我尝试过了:
@Service
public class SomeService implements ISomeService {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate npjt;
@Transactional
private void databaseChanges() throws Exception {
A(); //update.
B(); //insert
}
}
我的 AppConfig 类:
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
@Configuration
public class AppConfig {
@Autowired
private DataSource dataSource;
@Bean
public NamedParameterJdbcTemplate namedParameterJdbcTemplate() {
return new NamedParameterJdbcTemplate(dataSource);
}
}
‘A’ 进行更新。从“B”中抛出异常。 “A”所做的更新未回滚。
从我读到的,我知道我没有正确使用@Transactional。我阅读并尝试了几篇博客文章和 stackverflow 问答,但都没有成功解决我的问题。
有什么建议么?
编辑
有一个调用 databaseChanges() 方法的方法
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
哪个方法应该用@Transactional注解,
变化()?数据库更改()?
原文由 lolo 发布,翻译遵循 CC BY-SA 4.0 许可协议
@Transactional
spring 中的注释通过将对象包装在代理中来工作,代理又在事务中包装用@Transactional
注释的方法。由于该注释不适用于私有方法(如在您的示例中),因为 私有方法不能被继承 =>它们不能被包装(如果您将声明性事务与 aspectj 一起使用,那么与代理相关的警告是不正确的以下不适用)。这是
@Transactional
spring 魔法如何工作的基本解释。你写了:
但这是注入 bean 时实际得到的:
这有局限性。它们不适用于
@PostConstruct
方法,因为它们在代理对象之前被调用。即使你配置正确,默认情况下,事务只会在 未经检查 的异常上回滚。使用@Transactional(rollbackFor={CustomCheckedException.class})
如果您需要回滚某些已检查的异常。我知道另一个经常遇到的警告:
@Transactional
方法只有在“从外部”调用它时才有效,在以下示例中b()
不会包含在事务中:这也是因为
@Transactional
通过代理您的对象来工作。在上面的示例a()
将调用X.b()
不是增强的“spring代理”方法b()
所以不会有交易。作为解决方法,您必须从另一个 bean 调用b()
。当您遇到这些警告中的任何一个并且不能使用建议的解决方法(使方法非私有或从另一个 bean 调用
b()
)时,您可以使用TransactionTemplate
而不是声明式事务:更新
使用上面的信息回答 OP 更新的问题。
确保
changes()
被称为 bean 的“外部”,而不是来自类本身并且在实例化上下文之后(例如,这不是afterPropertiesSet()
或@PostConstruct
).了解默认情况下 spring rollbacks 事务仅针对未检查的异常(尝试在 rollbackFor checked exceptions 列表中更具体)。