在Spring Boot中,当我们使用了`spring-boot-starter-jdbc`或`spring-boot-starter-data-jpa`依赖的时候,框 架会自动默认分别注入`DataSourceTransactionManager`或`JpaTransactionManager`。所以我们不需要任何额外 配置就可以用`@Transactional`注解进行事务的使用。
在该样例工程中,我们引入了spring-data-jpa,并创建了`User`实体以及对User的数据访 问对象`UserRepository`,在`ApplicationTest`类中实现了使用`UserRepository`进行数据读写的单元测试用例.
通过定义User的name属性长度为5,这样通过创建时User实体的name属性超长就可以触发异常产生。
~~~
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
@Column(nullable = false, length = 5)
private String name;
@Column(nullable = false)
private Integer age;
// 省略构造函数、getter和setter
}
~~~
测试用例
~~~
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(Application.class)
public class ApplicationTests {
@Autowired
private UserRepository userRepository;
@Test
public void test() throws Exception {
// 创建10条记录
userRepository.save(new User("AAA", 10));
userRepository.save(new User("BBB", 20));
userRepository.save(new User("CCC", 30));
userRepository.save(new User("DDD", 40));
userRepository.save(new User("EEE", 50));
userRepository.save(new User("FFF", 60));
userRepository.save(new User("GGG", 70));
userRepository.save(new User("HHHHHHHHHH", 80));
userRepository.save(new User("III", 90));
userRepository.save(new User("JJJ", 100));
// 省略后续的一些验证操作
}
}
~~~
执行测试用例
~~~
2016-05-27 10:30:35.948 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1406, SQLState: 22001
2016-05-27 10:30:35.948 ERROR 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data truncation: Data too long for column 'name' at row 1
2016-05-27 10:30:35.951 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Warning Code: 1406, SQLState: HY000
2016-05-27 10:30:35.951 WARN 2660 --- [ main] o.h.engine.jdbc.spi.SqlExceptionHelper : Data too long for column 'name' at row 1
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement
~~~
此时查数据库中,创建了name从AAA到GGG的记录,没有HHHHHHHHHH、III、JJJ的记录。而若这是一个希望保证完整性操作的情况 下,AAA到GGG的记录希望能在发生异常的时候被回退,这时候就可以使用事务让它实现回退,做法非常简单,我们只需要在test函数上添加 `@Transactional` 注解即可。
~~~
@Test
@Transactional
public void test() throws Exception {
// 省略测试内容
}
~~~
这里主要通过单元测试演示了如何使用 @Transactional 注解来声明一个函数需要被事务管理,通常我们单元测试为了保证每个测试之间的数据独立,会使用 `@Rollback` 注解让每个单元测试都能在结束时回滚。而真正在开发业务逻辑时,我们通常在service层接口中使用 `@Transactional` 来对各个业务逻辑进行事务管理的配置,例如:
~~~
public interface UserService {
@Transactional
User login(String name, String password);
}
~~~
spring Boot 使用事务非常简单,首先使用注解 `@EnableTransactionManagement` 开启事务支持后,然后在访问数据库的Service方法上添加注解 `@Transactional `便可。
关于事务管理器,不管是JPA还是JDBC等都实现自接口 `PlatformTransactionManager` 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 `DataSourceTransactionManager` 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 `JpaTransactionManager` 实例。
你可以在启动类中添加如下方法,Debug测试,就能知道自动注入的是 `PlatformTransactionManager` 接口的哪个实现类。
~~~
@EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication {
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager){
System.out.println(">>>>>>>>>>" + platformTransactionManager.getClass().getName());
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
~~~
这些SpringBoot为我们自动做了,这些对我们并不透明,如果你项目做的比较大,添加的持久化依赖比较多,我们还是会选择人为的指定使用哪个事务管理器。
代码如下:
~~~
@EnableTransactionManagement
@SpringBootApplication
public class ProfiledemoApplication {
// 其中 dataSource 框架会自动为我们注入
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager) {
System.out.println(">>>>>>>>>>" + platformTransactionManager.getClass().getName());
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
~~~
在Spring容器中,我们手工注解@Bean 将被优先加载,框架不会重新实例化其他的 PlatformTransactionManager 实现类。
然后在Service中,被 @Transactional 注解的方法,将支持事务。如果注解在类上,则整个类的所有方法都默认支持事务。
对于同一个工程中存在多个事务管理器要怎么处理,请看下面的实例,具体说明请看代码中的注释。
@EnableTransactionManagement // 开启注解事务管理,等同于xml配置文件中的 <tx:annotation-driven />
@SpringBootApplication
public class ProfiledemoApplication implements TransactionManagementConfigurer {
~~~
@Resource(name="txManager2")
private PlatformTransactionManager txManager2;
// 创建事务管理器1
@Bean(name = "txManager1")
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
// 创建事务管理器2
@Bean(name = "txManager2")
public PlatformTransactionManager txManager2(EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
// 实现接口 TransactionManagementConfigurer 方法,其返回值代表在拥有多个事务管理器的情况下默认使用的事务管理器
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return txManager2;
}
public static void main(String[] args) {
SpringApplication.run(ProfiledemoApplication.class, args);
}
}
~~~
~~~
@Component
public class DevSendMessage implements SendMessage {
// 使用value具体指定使用哪个事务管理器
@Transactional(value="txManager1")
@Override
public void send() {
System.out.println(">>>>>>>>Dev Send()<<<<<<<<");
send2();
}
// 在存在多个事务管理器的情况下,如果不使用value具体指定
// 则默认使用方法 annotationDrivenTransactionManager() 返回的事务管理器
@Transactional
public void send2() {
System.out.println(">>>>>>>>Dev Send2()<<<<<<<<");
}
}
~~~
>注:
如果Spring容器中存在多个 PlatformTransactionManager 实例,并且没有实现接口 TransactionManagementConfigurer 指定默认值,在我们在方法上使用注解 @Transactional 的时候,就必须要用value指定,如果不指定,则会抛出异常。
>对于系统需要提供默认事务管理的情况下,实现接口 TransactionManagementConfigurer 指定。
>对有的系统,为了避免不必要的问题,在业务中必须要明确指定 @Transactional 的 value 值的情况下。不建议实现接口 TransactionManagementConfigurer,这样控制台会明确抛出异常,开发人员就不会忘记主动指定。
>
除了指定不同的事务管理器之后,还能对事务进行隔离级别和传播行为的控制,下面分别详细解释:
## 隔离级别
隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。
我们可以看 org.springframework.transaction.annotation.Isolation 枚举类中定义了五个表示隔离级别的值:
~~~
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
}
~~~
* DEFAULT :这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是: READ_COMMITTED 。
* READ_UNCOMMITTED :该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。
* READ_COMMITTED :该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。
* REPEATABLE_READ :该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。
* SERIALIZABLE :所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
指定方法:通过使用 isolation 属性设置,例如:
`@Transactional(isolation = Isolation.DEFAULT)`
## 传播行为
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
我们可以看 org.springframework.transaction.annotation.Propagation 枚举类中定义了6个表示传播行为的枚举值:
~~~
public enum Propagation {
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
}
~~~
* REQUIRED :如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
* SUPPORTS :如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
* MANDATORY :如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。
* REQUIRES_NEW :创建一个新的事务,如果当前存在事务,则把当前事务挂起。
* NOT_SUPPORTED :以非事务方式运行,如果当前存在事务,则把当前事务挂起。
* NEVER :以非事务方式运行,如果当前存在事务,则抛出异常。
* NESTED :如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED
指定方法:通过使用 propagation 属性设置,例如:
`@Transactional(propagation = Propagation.REQUIRED)`
- I. Spring Boot Documentation
- 1.关于文档
- 2. 获取帮助
- 3.第一步
- 4.使用spring boot
- 5.学习spring boot的特点
- 6.转移到生产
- 7.高级话题
- II. Getting Started
- 8.引入spring boot
- 9.系统需要
- 9.1 Servlet容器
- 10.安装spring boot
- 10.1 java开发者安装说明
- 10.1.1 Maven Installation
- 10.2 Installing the Spring Boot CLI TODO
- 10.3 从早期的版本升级
- 11.开发你的第一个spring boot应用
- 11.1 创建POM
- 11.2 添加Classpath依赖
- 11.3 写代码
- 11.3.1 @RestController 和@RequestMapping 注解
- 11.3.2 @EnableAutoConfiguration注解
- 11.3.3 main方法
- 11.4运行 Example
- 11.5 创建可执行的Jar
- 12.下一步要阅读的
- III. Using Spring Boot
- 13.构建系统
- 13.1 依赖管理
- 13.2 Maven
- 13.2.1 继承Starter Parent
- 13.2.2 不使用Spring Boot的Parent POM
- 13.2.3 使用Spring Boot Maven 插件
- 13.3. Gradle TODO
- 13.4. Ant TODO
- 13.5. Starters TODO
- 14.结构化代码
- 14.1使用“default”包
- 14.2 主应用程序类的位置
- 15.配置类
- 15.1 引入新增的配置类
- 15.2 引入xml配置
- 16.自动化配置
- 16.1 逐渐替换自动配置
- 16.2 禁用特定的配置类
- 17.spring的bean和依赖配置
- 18.使用@SpringBootApplication注解
- 19.运行的你的程序
- 19.1 在IDE中运行
- 19.2 运行打包的jar
- 19.3 使用maven插件运行
- 19.4 使用Gradle 插件 TODO
- 19.5 热交换
- 20.开发者工具
- 20.1 默认属性
- 20.2 自动重启
- 20.2.1 记录环境评估的更改
- 20.2.2 排除Resources
- 20.2.3 监视其他路径
- 20.2.4 禁止重启
- 20.2.5 使用触发文件
- 20.2.6 自定义重启类加载器
- 20.2.7 了解限制
- 20.3 实时加载
- 20.4 全局设置
- 20.5 远程应用
- 20.5.1 运行远程客户端
- 20.5.2 远程更新
- 21.把你的应用打包到生产
- 22.下一步要阅读的
- IV. Spring Boot features
- 23.spring应用
- 23.1 启动失败
- 23.10 管理功能
- 23.8 使用ApplicationRunner 或 CommandLineRunner
- 24.可扩展的配置
- 24.1 配置随机值
- 24.2 访问命令行属性
- 24.3 配置文件
- 24.4 特定配置文件
- 24.5 属性的占位符
- 24.6 使用yaml替代properties
- 24.7 类型安全的配置属性
- 24.7.1 第三方配置
- 24.7.2 灵活的绑定
- 25.外部的配置profiles
- 25.1 追加可用的外部配置
- 25.2 编程式的设置外部配置
- 25.3 外部配置文件
- 26.日志
- 26.1 日志格式
- 26.2 控制台输出
- 26.2.1颜色代码数据
- 26.3 文件输出
- 26.4 日志级别
- 26.5 自定义日志配置
- 26.6 Logback扩展
- 26.6.1特定于配置文件的配置
- 26.6.2 环境属性
- 26.6.3 logback.xml配置说明
- 27.开发web应用
- 27.1 spring mvc
- 27.1.1 Spring MVC自动配置
- 27.1.2 HttpMessageConverters
- 27.1.3 定制JSON序列号和反序列化
- 27.1.4 MessageCodesResolver
- 27.1.5 静态内容
- 27.1.6 欢迎页面
- 27.1.7 定制Favicon
- 27.1.8 路径匹配和内容判断
- 27.1.9 ConfigurableWebBindingInitializer
- 27.1.10 模板引擎
- 27.1.11 处理错误
- 27.1.12 Spring HATEOAS
- 27.1.13 跨域支持
- 27.2 spring webflux
- 28.安全
- 28.1 mvc安全
- httpSecurity,webSecurity,authenticationManager
- 29.使用SQL数据库
- 29.1 配置数据源
- 29.1.1 嵌入式数据库支持
- 29.1.2 可用于生产的数据库
- 29.1.3 从JNDI DataSource获取连接
- 30.使用NoSql技术
- 30.1 Redis
- 30.1.1 连接Redis
- 31.缓存
- 32.消息
- 32.1 jms
- 32.2 amqp
- 32.2.1 支持RabbitMQ
- 32.2.2 发送消息
- 32.2.3 接收消息
- 33.使用RestTemplate调用REST服务
- 34.使用WebClient调用REST 服务
- 35.验证
- 36.发邮件
- 37.使用JTA的分布式事务
- 37.01 结合spring框架的本地事务
- 38. Hazelcast
- 39.定时任务
- 40.spring集成
- 41.spring会话
- 42.JMX的监控和管理
- 43.测试
- 43.1测试范围的依赖
- 43.2 测试Spring应用
- 43.2 测试spring boot应用
- 44. WebSockets
- 45. Web Services
- 46.创建你自己的自动化配置
- 47. Kotlin的支持
- 48.下一步要阅读的
- V. Spring Boot Actuator: Production-ready features
- 49.开启生产用的特性
- 50.端点
- 50.1 启动端点
- 50.2 暴露端点
- 50.3 安全的http端点
- 51.HTTP的监控和管理
- 52.JMX的监控和管理
- 53.日志
- 54.度量
- 55.审计
- 56.HTTP追踪
- 57.过程监控
- 58.上云的支持
- 59.下一步要阅读的
- VI. Deploying Spring Boot Applications
- 60.云端的部署
- 61.安装spring boot应用
- 62.下一步要阅读的
- VII. Spring Boot CLI
- 63.安装CTL
- 64.使用CTL
- 65.使用Groovy Beans DSL开发应用
- 66.使用settings.xml配置CTL
- 67.下一步要阅读的
- VIII. Build tool plugins
- 68. Spring Boot Maven插件
- 69. Spring Boot Gradle插件
- 70. Spring Boot AntLib模块
- 71.其他构建系统的支持
- 72.下一步要阅读的
- IX. ‘How-to’ guides
- 73.spring boot应用
- 74.属性和配置
- 75.嵌入式的web服务器
- 76. Spring MVC
- 76.1 写JSON风格的服务
- 76.2 写XML风格的服务
- 76.3 定制Jackson ObjectMapper
- 76.4 定制@ResponseBody呈现
- 76.5 处理文件上传
- 77. Jersey
- 78. HTTP Clients
- 79. 日志
- 80. 数据访问
- 80.1 配置自己的数据源
- 81. 数据库初始化
- 82. 消息
- 83. 批处理应用
- 84. Actuator
- 85. 安全
- 86. 热交换
- 87.构建
- 88.传统开发
- X. Appendices
- A.通用的配置
- B. 配置元信息
- C.自动配置类
- D. 测试自动配置注解
- E. 可执行的jar格式
- F. 依赖的版本