lol*_*olo 12 java oracle jdbctemplate spring-transactions spring-boot
我有一个方法'databaseChanges',它以迭代的方式调用2个操作:A,B.'A'第一,'B'最后."A"和"B"可以是Ç reate,û PDATE d elete功能在我的永久存储,Oracle数据库11g.
比方说,
'A'更新表Users中的记录,属性zip,其中id = 1.
'B'在表爱好中插入记录.
场景:已调用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);
}
}
Run Code Online (Sandbox Code Playgroud)
问题:我没有连接(请参阅带问题的标签)
我试过了:
@Service
public class SomeService implements ISomeService {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate npjt;
@Transactional
private void databaseChanges() throws Exception {
A(); //update.
B(); //insert
}
}
Run Code Online (Sandbox Code Playgroud)
我的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);
}
}
Run Code Online (Sandbox Code Playgroud)
'A'进行更新.来自'B'的例外被抛出.由'A'进行的更新未被回滚.
从我读到的,我明白我没有正确使用@Transactional.我阅读并尝试了几篇博客文章和stackverflow问答,但没有成功解决我的问题.
有什么建议?
编辑
有一种方法可以调用databaseChanges()方法
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Run Code Online (Sandbox Code Playgroud)
哪个方法应该用@Transactional注释,
变化()?databaseChanges()?
fre*_*ger 20
@Transactionalspring中的注释通过将对象包装在代理中来工作,而代理又包装@Transactional在事务中注释的方法.因为该注释不适用于私有方法(如在您的示例中),因为私有方法不能被继承 =>它们不能被包装(如果您使用带有aspectj的声明式事务,那么这不是真的,那么代理相关的警告以下不适用).
这是@Transactional春天魔法如何运作的基本解释.
你写了:
class A {
@Transactional
public void method() {
}
}
Run Code Online (Sandbox Code Playgroud)
但这是你注入bean时实际获得的:
class ProxiedA extends A {
private final A a;
public ProxiedA(A a) {
this.a = a;
}
@Override
public void method() {
try {
// open transaction ...
a.method();
// commit transaction
} catch (RuntimeException e) {
// rollback transaction
} catch (Exception e) {
// commit transaction
}
}
}
Run Code Online (Sandbox Code Playgroud)
这有局限性.它们不适用于@PostConstruct方法,因为它们在代理对象之前被调用.即使您正确配置了所有内容,默认情况下,事务仅在未经检查的异常上回滚.@Transactional(rollbackFor={CustomCheckedException.class})如果需要在某些已检查的异常上进行回滚,请使用
另一个经常遇到的警告我知道:
@Transactional只有在"从外部"调用它时,方法才会起作用,以下示例b()不会包含在事务中:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
Run Code Online (Sandbox Code Playgroud)
这也是因为@Transactional代理你的对象.在上面的示例中,a()将调用X.b()不是增强的"spring proxy"方法,b()因此不会有事务.作为一种解决方法,您必须b()从另一个bean 调用.
当您遇到任何这些警告并且无法使用建议的解决方法(make方法非私有或b()从另一个bean 调用)时,您可以使用TransactionTemplate而不是声明性事务:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Run Code Online (Sandbox Code Playgroud)
更新
使用上面的信息回答OP更新的问题.
哪个方法应该用@Transactional注释:changes()?databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Run Code Online (Sandbox Code Playgroud)
确保changes()从bean的"外部"调用,而不是从类本身调用并在实例化上下文之后(例如,这不是 afterPropertiesSet()或@PostConstruct注释方法).默认情况下,只知道未经检查的异常的spring rollbacks事务(尝试在rollbackFor checked exception列表中更具体).
任何
RuntimeException触发器都会回滚,而任何已检查的Exception不会。
这是所有Spring事务API的共同行为。默认情况下,如果RuntimeException从事务代码内抛出a ,则事务将回滚。如果RuntimeException引发了检查异常(即不是),则事务将不会回滚。
这取决于您要获取内部databaseChanges函数的异常。因此,为了捕获所有异常,您需要做的就是添加rollbackFor = Exception.class
应该在服务类上进行的更改,代码将如下所示:
@Service
public class SomeService implements ISomeService {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate npjt;
@Transactional(rollbackFor = Exception.class)
private void databaseChanges() throws Exception {
A(); //update
B(); //insert
}
}
Run Code Online (Sandbox Code Playgroud)
另外,您可以使用它做一些不错的事情,因此不必每次都写rollbackFor = Exception.class。您可以通过编写自己的自定义注释来实现:
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor = Exception.class)
@Documented
public @interface CustomTransactional {
}
Run Code Online (Sandbox Code Playgroud)
最终代码将如下所示:
@Service
public class SomeService implements ISomeService {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate npjt;
@CustomTransactional
private void databaseChanges() throws Exception {
A(); //update
B(); //insert
}
}
Run Code Online (Sandbox Code Playgroud)