我有一个方法,数据库变化,它以迭代的方式调用2个操作:A,B。第一个,最后一个。'A'
让我们说,
'a'更新表用户属性zip中的记录,其中id=1。
“B”在表中插入记录。
场景:数据库更改方法被调用,'A'操作并更新记录。'B'操作并尝试插入一条记录,发生了一些事情,抛出了一个异常,异常正在冒泡到数据库更改方法。
预期:“A”和“B”没有改变任何东西。“A”执行的更新将被回滚。”B'没有改变什么,嗯。。。有一个例外。
实际:“更新似乎未回滚。”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 Q
有什么建议吗?
编辑
有一个方法调用databaseChanges()方法
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
哪个方法应该用@Transactional注释,
变化()?数据库更改
您呈现的第一个代码是用于UserTransaction的,即应用程序必须执行事务管理。通常,您希望容器处理这些问题,并使用@Transactional注释。我认为在你的情况下,问题可能是,你有一个私有方法的注释。我会把注释移到班级级别
@Transactional
public class MyFacade {
public void databaseChanges() throws Exception {
A(); //update.
B(); //insert
}
然后它应该正确地回滚。您可以在这里找到更多详细信息Spring@Transactional属性是否在私有方法上工作?
任何RuntimeException
都会触发回滚,而任何选中的异常都不会触发回滚。
这是所有Spring事务API的常见行为。默认情况下,如果从事务代码中抛出运行时异常
,则事务将回滚。如果抛出选中的异常(即不是运行时异常
),则事务将不会回滚。
这取决于您在数据库更改函数中获得的异常。因此,为了捕获所有异常,您需要做的就是添加rollback For=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
}
}
此外,你可以用它做一些好事,所以不是所有的时间,你将不得不写rollback For=Exception.class
。您可以通过编写自己的自定义注释来实现这一点:
@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
}
}
spring中的@Transactional
注释通过将对象包装在代理中来工作,代理又将使用@Transactional
注释的方法包装在事务中。因为这个注释在私有方法上不起作用(如您的示例中),因为私有方法不能被继承=
下面是关于@Transactional
spring magic如何工作的基本解释。
你写道:
class A {
@Transactional
public void method() {
}
}
但这是你注射豆子时实际得到的:
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()
)时,您可以使用TransactionTem板
而不是声明性事务:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
使现代化
使用上述信息回答OP更新的问题。
哪个方法应该被注释为@Transactional:更改()?数据库更改
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
确保changes()
是从bean的“外部”调用的,而不是从类本身以及在实例化上下文之后调用的(例如,这不是afterPropertieSet()
或@PostConstruct
带注释的方法)。了解spring在默认情况下仅对未检查的异常回滚事务(请尝试在rollbackFor checked exceptions列表中更加具体)。
问题内容: 我有一个方法“ databaseChanges”,它以迭代方式调用2个操作:A,B。首先是“ A”,最后是“ B”。“A”和“B”可以是 Ç reate, ü PDATE d 在我的持久存储elete功能,Oracle数据库11g。 比方说 ‘A’更新表Users(属性zip,其中id = 1)中的记录。 “ B”在表爱好中插入一条记录。 场景: 调用了databaseChanges方
本文向大家介绍分析`('b' + 'a' + +'a' + 'a').toLowerCase()`返回的结果相关面试题,主要包含被问及分析`('b' + 'a' + +'a' + 'a').toLowerCase()`返回的结果时的应答技巧和注意事项,需要的朋友参考一下 那个多出来的 + 是一元操作符,操作数是后面那个 'a',它被转成了 number 。 因此加出来是 'baNaNa',toLo
问题内容: 这是我的第一个问题,我开始学习Python。之间有什么区别: 和 在下面的示例中编写时,它显示不同的结果。 和 问题答案: 在中,在将右侧的表达式赋给左侧之前对其求值。因此,它等效于: 在第二个示例中,运行时已更改的值。因此,结果是不同的。
我尝试了一些代码在Java中交换两个整数,而不使用第三个变量,即使用XOR。 以下是我尝试的两个交换函数: 该代码产生的输出如下: 我很想知道,为什么会有这样的说法: 和这个不一样?
问题内容: 我尝试了一些代码,使用XOR在Java中交换两个整数而不使用第三个变量。 这是我尝试的两个交换函数: 这段代码产生的输出是这样的: 我很好奇,为什么这样说: 与这个不同吗? 问题答案: 问题是评估的顺序: 参见JLS第15.26.2节 首先,对左操作数求值以产生一个变量。 如果该评估突然完成,则赋值表达式由于相同的原因而突然完成;右边的操作数不会被评估,并且不会发生赋值。 否则,将保存
问题内容: 今天,我发现了python语言一个有趣的“功能”,这让我感到非常悲伤。 那个怎么样?我以为两者是等同的!更糟糕的是,这是我调试时遇到的麻烦的代码 WTF!我的代码中包含列表和字典,并且想知道我到底怎么把dict的键附加到列表上而又没有调用.keys()。事实证明,这就是方法。 我认为这两个陈述是等效的。即使忽略这一点,我也可以理解将字符串追加到列表的方式(因为字符串只是字符数组),但是