- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
事务定义:
将一组操作封装成一个执行单元,要么全部成功,要么全部失败。
事务的意义:
当执行某个操作,例如支付操作时(分为先将钱从个人账户扣除和将他人账户新增两个操作),如果这两个操作不能同时成功或者失败,那么就会出现财产问题。而使用事务就能够很好的解决这个问题。
Spring 中的事务操作分为两类:
Spring 手动操作事务分为三个步骤:
SpringBoot 内置了两个对象,可以用来处理事务:
具体代码实现如下:
@RestController
@RequestMapping("user")
public class UserController {
// JDBC 事务管理器
@Resource
private DataSourceTransactionManager dataSourceTransactionManager;
// 定义事务属性
@Resource
private TransactionDefinition transactionDefinition;
@RequestMapping("/test1")
public String test1(){
// 开启事务
TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
// 执行数据库操作
// 提交事务
dataSourceTransactionManager.commit(transaction);
// 回滚事务
dataSourceTransactionManager.rollback(transaction);
return "测试完成!";
}
}
声明式事务的实现相较于手动实现要简单很多,只需要在需要添加事务的方法上加上 @Transactional
注解就可以,无需手动开启事务和提交事务,进入方法时会自动开启事务,方法执行完成会自动提交事务,如果中途发生了没有处理的异常就会自动回滚事务。
具体代码实现如下:
@RestController
@RequestMapping("user")
public class UserController {
@Transactional
@RequestMapping("/test2")
public String test2(){
// 执行数据库操作
return "测试完成!";
}
}
当我们在执行完数据库操作后,有一个语句出现异常,通过 @Transactional 注解就能进行回滚。
通过上文我们了解到,在需要的方法上添加 @Transactional 注解,就能自动开启事务。接下来将会具体了解下 @Transactional 的使用细节。
@Fransactional 可以用来修饰方法或类:
参数 | 作用 |
---|---|
value | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。 |
transactionManager | 当配置了多个事务管理器时,可以使用该属性指定选择哪个事务管理器。 |
propagation | 事务的传播行为,默认值为 Propagation.REQUIRED。 |
isolation | 事务的隔离级别,默认值为 Isolation.DEFAULT。 |
timeout | 事务的超时时间,默认值为-1(表示没有超时时间)。如果超过该超时时间显示但事务还没有完成,则自动回滚事务。 |
readOnly | 指定事务是否为只读事务,默认值为 false。为了忽略那些不需要事务的方法,比如读取数据,可以设置为 true。 |
rollbackFor | 用于指定能够被触发事务回滚的异常类型,可以指定多个异常类型。 |
rollbackForClassName | 用于指定能够被触发事务回滚的异常类型,可以指定多个异常类型。 |
noRollbackFor | 抛出异常的类型,不回滚事务,也可以指定多个异常类型。 |
noRollbackForClassName | 抛出异常的类型,不回滚事务,也可以指定多个异常类型。 |
注意: rollbackFor 是将指定的非运行时异常进行回滚。
@Transactional 在异常被捕获的情况下,不会进行事务的自动回滚。
注意: 默认情况下,Spring 中的事务如果遇到运行时异常,事务是会进行回滚的,但遇到非运行时异常,事务不会自动回滚。可以设置 rollbackFor 来解决非运行时异常不会被回滚的问题。
示例代码如下:
@RequestMapping("/test3")
@Transactional
public String test3(@RequestParam String username, @RequestParam String pwd) {
// 插⼊数据库
int result = userService.addUser(username, pwd);
try {
// 执⾏了异常代码
int i = 10 / 0;
} catch (Exception e) {
}
return "测试完成!";
}
以上代码虽然出现了算数异常,但是由于主动捕获了,因此不会进行事物的回滚,数据库中会插入该条数据。
如果要解决出现异常事务不能自动回滚的问题,以下提供两种解决方案:
@RequestMapping("/test3")
@Transactional
public String test3(@RequestParam String username, @RequestParam String pwd) {
// 插⼊数据库
int result = userService.addUser(username, pwd);
try {
// 执⾏了异常代码
int i = 10 / 0;
} catch (Exception e) {
// 将异常重新抛出
throw e;
}
return "测试完成!";
}
TransactionAspectSupport.currentTransactionStatus()
得到当前的事务,然后设置回滚方法 setRollbackOnly
就可以实现回滚。示例代码如下:@RequestMapping("/test3")
@Transactional
public String test3(@RequestParam String username, @RequestParam String pwd) {
// 插⼊数据库
int result = userService.addUser(username, pwd);
try {
// 执⾏了异常代码
int i = 10 / 0;
} catch (Exception e) {
// 手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return "测试完成!";
}
@Transactional 是基于 AOP 实现的,AOP 又是使用了动态代理实现的。如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理;如果目标对象没有实现接口,会使用 CGLIB 动态代理。@Transactional 在开始执行业务之前,通过代理先开启事务,在执行成功之后再提交事务。如果中途遇到异常,则回滚事务。
@Transactional 实现思路:
@Transactional 具体执行细节:
事务有4大特性,简称为 ACID,分别如下:
在这四种特性中,只有隔离性可以设置,通过设置事务的隔离级别可以用来保障多个并发事务执行更加可控,更符合操作者的预期,是防止其它的事务影响当前事务执行的一种策略。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | √ | √ | √ |
读已提交 | × | √ | × |
可重复度 | × | × | √ |
串行化 | × | × | × |
在数据库中可以通过以下 SQL 查询全局事务隔离级别和当前连接的事务隔离级别:
select @@global.tx_isolation,@@tx_isolation;
Spring 中事务隔离级别可以通过 @Transactional 的 Isolation 属性进行设置。
Spring 事务传播机制定义了多个包含了事务的方法在相互调用时,事务是如何在这些方法之间进行传递的。
嵌套事务和加入事务的区别:
如果有人能解释这个注释的作用以及我们何时使用它: @Transactional(propagation=Propagation.REQUIRED) 谢谢 最佳答案 如果您需要在 Spring Docs
我有一个页面,它有一个 keydown 事件监听器,用于监听 Escape 键,以便返回。我还有一个简单的模态类,它也监听 Escape 键以关闭它。主页监听器检查模式是否打开,如果打开,则不执行任何
我想在模型中设置默认变量名称 T (=xx) - 将该模型拖到新模型中并在其中定义变量 xx。我收到错误消息:使用未声明的变量 xx。 这是子模型 model test parameter Rea
在 android 2.x 浏览器中查看此示例..它是在我的应用程序中复制场景的示例.. http://johnchacko.net/samples/tap.html 它是关于监听“tap”并从监听器
如您所见,我正在尝试将 GatewayConnectionFailedException 传播到我的 UI。我希望此代码捕获除异常之外的所有内容,我希望表示层捕获该异常以通知用户数据库是问题所在,以便
我目前正在尝试让可执行文件与它需要的所有依赖项正确链接。 这是依赖项的示例结构: exe -> libA -> libB exe和 libA有自己的存储库。 exe拉入libA像这样的东西: add_
有什么方法可以调用带有单个参数的 Scala 函数,给定一个数组 (类似于 JavaScript Spreads在 ECMAScript 6) 中? ys = [10.0, 2.72, -3.14]
我有一个小型静态库,它需要 boost 头文件,并且需要包含目录中的“include”目录。 ... add_library(alib STATIC ...) target_include_direc
我有一些 promise 可以返回对象。 现在我想将它们合并/扩展为一个新对象,因此我使用 Lodash's extend . var whenEverythingIsDone = Promise.a
这是我认为人们通常希望在 Scala 中做的事情,但如果我能在任何地方找到一个例子,我就该死了。 这段代码由于类型删除而无法编译,但它演示了我正在努力完成的事情: def parse[T](json:
这是我认为人们通常希望在 Scala 中做的事情,但如果我能在任何地方找到一个例子,我就该死了。 这段代码由于类型删除而无法编译,但它演示了我正在努力完成的事情: def parse[T](json:
我们有大量 MOSS 2007 站点需要添加大量的 javascript。我编辑、 checkin 、发布并批准了对 default.master 的更改,更改反射(reflect)在根网站上,但没有
请看一下下面的 fiddle :http://jsfiddle.net/K9NjY/ 我在这段代码上花了 3-4 个小时,并将其缩小到最短的版本,但现在我陷入了困境。 问题:1. 点击“divOne”
我读到如果在流程中抛出异常,框架要做的第一件事就是检查消息头中的错误 channel 属性。总是这样吗? 在我的特殊情况下,我将自定义错误 channel 分配给消息 header ,但该消息似乎已向
创建一个小的 C++ 大型精度类,一切似乎都运行良好,但是添加,如果我将 0xffffffff 和 0x04 加在一起,我会得到 0xffff0003,而我应该得到 0x0100000003。这是有问
我正在尝试重新创建 Dan Abramov 类(class)中的 Redux 示例。传播{...store.getState()}在应用程序级别不起作用,Redux 正在更改状态并且 React 不会
考虑一个需要很长时间的事务。在此期间,我想对 TableSmall 执行一些小更新。 ,它应该立即执行,并且主事务的回滚不应该回滚那些小的更新。 我当前的问题是这些小更新将锁定 TableSmall\
我需要对现有函数进行修改,具有一些 const 输入参数: int f(const owntype *r1, const owntype *r2) 为了做到这一点,我想调用一个使用相同类型但没有 co
我有一个带有 ViewModel 的 WPF UserControl: 这个 UserControl 有一个 De
我试图在收到这样的短信时不传播 public class SMSReceiver extends BroadcastReceiver { @Override public void onRec
我是一名优秀的程序员,十分优秀!