Java 如果B出错,请回滚A。spring引导,jdbctemplate
我有一个方法“databaseChanges”,它以迭代的方式调用2个操作:a、BA‘第一’,B‘最后。 “A”和“B”可以是我的持久性存储Oracle Database 11g中的CCreate、UpdateDelete功能 比方说 “A”更新表用户中的记录,属性为zip,其中id=1 “B”在表中插入记录 场景:调用databaseChanges方法时,“A”操作并更新记录B'操作并尝试插入一条记录,发生了一些情况,抛出了一个异常,异常冒泡到databaseChanges方法 预期:“A”和“B”没有改变任何东西。“A”执行的更新将被回滚。”B'没有改变什么,嗯。。。有一个例外 实际:“A”更新似乎未回滚B'没有改变什么,嗯。。。有一个例外Java 如果B出错,请回滚A。spring引导,jdbctemplate,java,oracle,spring-boot,jdbctemplate,spring-transactions,Java,Oracle,Spring Boot,Jdbctemplate,Spring Transactions,我有一个方法“databaseChanges”,它以迭代的方式调用2个操作:a、BA‘第一’,B‘最后。 “A”和“B”可以是我的持久性存储Oracle Database 11g中的CCreate、UpdateDelete功能 比方说 “A”更新表用户中的记录,属性为zip,其中id=1 “B”在表中插入记录 场景:调用databaseChanges方法时,“A”操作并更新记录B'操作并尝试插入一条记录,发生了一些情况,抛出了一个异常,异常冒泡到databaseChanges方法 预期:“A”和
一些代码
compile("org.springframework:spring-tx")
如果我有连接,我会做如下事情:
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()方法 哪个方法应该用@Transactional注释
更改()?databaseChanges()?您提供的第一个代码是针对UserTransactions的,即应用程序必须执行事务管理。通常,您希望容器处理这个问题,并使用@Transactional注释。我认为您案例中的问题可能是,您在私有方法上有注释。我会将注释移动到类级别
@Transactional
public class MyFacade {
public void databaseChanges() throws Exception {
A(); //update.
B(); //insert
}
然后它应该正确地回滚。你可以在这里找到更多细节
试试这个:
@TransactionManagement(TransactionManagementType.BEAN)
public class MyFacade {
@TransactionAttribute(TransactionAttribute.REQUIRES_NEW)
public void databaseChanges() throws Exception {
A(); //update.
B(); //insert
}
spring中的
@Transactional
注释通过将对象包装在代理中来工作,代理又将使用@Transactional
注释的方法包装在事务中。由于该注释无法在私有方法上工作(如您的示例中),因为私有方法不能被继承=>它们不能被包装(如果将声明性事务与一起使用,则不适用下面与代理相关的警告)
下面是关于@Transactional
spring magic如何工作的基本解释
你写道:
class A {
@Transactional
public void method() {
}
}
但这是注入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
}
}
}
这有局限性。它们不使用@PostConstruct
方法,因为它们是在代理对象之前调用的。即使您正确配置了所有事务,默认情况下,事务也只在未检查的异常上回滚。如果您需要回滚某些已检查的异常,请使用@Transactional(rollboor={CustomCheckedException.class})
我知道另一个经常遇到的警告:
@Transactional
方法只有在您将其称为“从外部”时才起作用,在下面的示例中,b()
将不会包装在事务中:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
这也是因为@Transactional
通过代理对象来工作。在上面的示例中,a()
将调用X.b()
而不是一个增强的“spring代理”方法b()
,因此将不存在事务。作为一种解决方法,您必须从另一个bean调用b()
当您遇到这些警告并且无法使用建议的解决方法(使方法非私有或从另一个bean调用b()
)时,您可以使用TransactionTemplate
而不是声明性事务:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
更新
compile("org.springframework:spring-tx")
使用上述信息回答OP更新的问题
哪个方法应该用@Transactional注释:
更改()?数据库更改()
确保
changes()
是从bean的“外部”调用的,而不是从类本身以及在实例化上下文之后调用的(例如,这不是afterPropertieSet()
或@PostConstruct
带注释的方法)。了解spring在默认情况下仅对未检查的异常回滚事务(请尝试在rollbackFor checked exceptions列表中更加具体) 您似乎缺少的是一个TransactionManager
。TransactionManager
的目的是能够管理数据库事务。有两种类型的事务,编程事务和声明事务。您所描述的是对通过注释的声明性事务的需求
因此,您需要为您的项目准备以下内容:
Spring事务依赖项(以Gradle为例)
在Spring引导配置中定义事务管理器
像这样的
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource)
{
return new DataSourceTransactionManager(dataSource);
}
您还需要添加@EnableTransactionManagement
注释(不确定这在较新版本的spring boot中是否是免费的)
@EnableTransactionManagement
public class AppConfig {
...
}
添加@Transactional
在这里,您将为要参与事务的方法添加@Transactional
注释
@Transactional
public void book(String... persons) {
for (String person : persons) {
log.info("Booking " + person + " in a seat...");
jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
}
};
注意,此方法<强>应该是public <强>而不是私有的。您可能需要考虑在公共方法调用<代码>数据库更改()/<代码> > < /p> 还有一些高级主题是关于
@Transactional
应该去哪里以及它的行为方式,因此最好先做一些工作,然后再稍后探索这个领域:)
在所有这些就绪(依赖项+transactionManager配置+注释)之后,事务应该相应地工作。@Transactional
public void book(String... persons) {
for (String person : persons) {
log.info("Booking " + person + " in a seat...");
jdbcTemplate.update("insert into BOOKINGS(FIRST_NAME) values (?)", person);
}
};
@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
}
}
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor = Exception.class)
@Documented
public @interface CustomTransactional {
}
@Service
public class SomeService implements ISomeService {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate npjt;
@CustomTransactional
private void databaseChanges() throws Exception {
A(); //update
B(); //insert
}
}