`

SpringCloud SpringBoot mybatis分布式微服务 -Spring Boot中的事务管理

阅读更多

 

 在Spring Boot中,当我们使用了spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖的时候,框架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager。所以我们不需要任何额外配置就可以用@Transactional注解进行事务的使用。了解springcloud架构可以加求求:三五三六二四七二五九。

 

我们以之前实现的《用spring-data-jpa访问数据库》的示例Chapter3-2-2作为基础工程进行事务的使用常识。

 

在该样例工程中(若对该数据访问方式不了解,可先阅读该文章),我们引入了spring-data-jpa,并创建了User实体以及对User的数据访问对象UserRepository,在ApplicationTest类中实现了使用UserRepository进行数据读写的单元测试用例,如下:

@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("HHH", 80));
		userRepository.save(new User("III", 90));
		userRepository.save(new User("JJJ", 100));
 
		// 省略后续的一些验证操作
	}
 
 
}

 可以看到,在这个单元测试用例中,使用UserRepository对象连续创建了10个User实体到数据库中,下面我们人为的来制造一些异常,看看会发生什么情况。

通过定义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
 
}

 修改测试用例中创建记录的语句,将一条记录的name长度超过5,如下:name为HHHHHHHHH的User对象将会抛出异常。

// 创建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));

 执行测试用例,可以看到控制台中抛出了如下异常,name字段超长:

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 {
 
    // 省略测试内容
 
}

 再来执行该测试用例,可以看到控制台中输出了回滚日志(Rolled back transaction for test context),

2016-05-27 10:35:32.210  WARN 5672 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 1406, SQLState: 22001
2016-05-27 10:35:32.210 ERROR 5672 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : Data truncation: Data too long for column 'name' at row 1
2016-05-27 10:35:32.213  WARN 5672 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Warning Code: 1406, SQLState: HY000
2016-05-27 10:35:32.213  WARN 5672 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : Data too long for column 'name' at row 1
2016-05-27 10:35:32.221  INFO 5672 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test context [DefaultTestContext@1d7a715 testClass = ApplicationTests, testInstance = com.didispace.ApplicationTests@95a785, testMethod = test@ApplicationTests, testException = org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; nested exception is org.hibernate.exception.DataException: could not execute statement, mergedContextConfiguration = [MergedContextConfiguration@11f39f9 testClass = ApplicationTests, locations = '{}', classes = '{class com.didispace.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.boot.test.SpringApplicationContextLoader', parent = [null]]].

 再看数据库中,User表就没有AAA到GGG的用户数据了,成功实现了自动回滚。

 

这里主要通过单元测试演示了如何使用@Transactional注解来声明一个函数需要被事务管理,通常我们单元测试为了保证每个测试之间的数据独立,会使用@Rollback注解让每个单元测试都能在结束时回滚。而真正在开发业务逻辑时,我们通常在service层接口中使用@Transactional来对各个业务逻辑进行事务管理的配置,例如:

 

public interface UserService {
    
    @Transactional
    User login(String name, String password);
    
}

 事务详解

上面的例子中我们使用了默认的事务配置,可以满足一些基本的事务需求,但是当我们项目较大较复杂时(比如,有多个数据源等),这时候需要在声明事务时,指定不同的事务管理器。对于不同数据源的事务管理配置可以见《Spring Boot多数据源配置与使用》中的设置。在声明事务时,只需要通过value属性指定配置的事务管理器名即可,例如:@Transactional(value="transactionManagerPrimary")。

 

除了指定不同的事务管理器之后,还能对事务进行隔离级别和传播行为的控制,下面分别详细解释:

 

#### 隔离级别

 

隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。

 

我们可以看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)

 

分享到:
评论

相关推荐

    dmall商城-SpringCloud构建的分布式电商项目-dmall.zip

    SpringCloud是基于Spring Boot进行快速构建云应用的工具集,它简化了分布式系统开发中的许多常见问题,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、...

    基于SpringCloud开发的在线教育系统

    在线教育平台服务端基于Spring Boot构建,采用Spring Cloud微服务框架。 持久层:MySQL、MongoDB、Redis、ElasticSearch 数据访问层:使用Spring Data JPA 、Mybatis、Spring Data Mongodb等 业务层:Spring IOC、...

    快速搭建SpringBoot工具spring-boot-plus-master.zip

    7. **微服务架构**:SpringBootPlus适合构建微服务架构,通过Spring Cloud组件,可以实现服务注册、发现、熔断、限流等功能,使复杂分布式系统的管理变得更为容易。 8. **错误处理与日志**:SpringBootPlus提供统一...

    spring boot 全面的样例代码

    - chapter9-1-4:[Spring Cloud构建微服务架构(四)分布式配置中心](http://blog.didispace.com/springcloud4/) - chapter9-1-5:[Spring Cloud构建微服务架构(五)服务网关]...

    Springboot快速开发模板 springboot-restful.rar

    在这个模板中,`pom.xml`文件包含了所有必要的SpringBoot起步依赖,如`spring-boot-starter-web`用于支持Web开发,`spring-boot-starter-data-jpa`用于数据库操作,以及可能的其他第三方库,如`spring-boot-starter-...

    Spring Cloud和Spring Boot视频教程

    它为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线等)中快速构建一些常见模式的能力。Spring Cloud兼容各种Spring Boot项目,使得开发人员可以轻松地将这些微服务组件整合...

    JAVAEE架构,springboot,springcloud微服务,ssm电商项目,分布式权限管理视频资源

    SpringCloud是基于Spring Boot实现的云应用开发工具集,它为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等)操作中...

    Java spring技术路线导图

    Spring Cloud是基于Spring Boot实现的云应用开发工具集,它为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态)中常用...

    SpringBoot专栏教程-邮件.pdf

    - Spring Cloud则是在Spring Boot的基础上,提供了一系列用于构建微服务架构的工具。 ### 快速入门 - **Maven工程创建**:通过Spring Initializr创建带有Spring Web依赖的新项目。 - **pom文件**:配置项目依赖,...

    spring-boot-examples-master.zip_gas4w6_springboot-example

    如果你对构建分布式系统感兴趣,这些示例还会展示如何利用Spring Cloud构建微服务架构,包括服务发现(Eureka)、负载均衡(Ribbon)、熔断机制(Hystrix)等。 通过深入学习和实践这些示例,你不仅可以掌握Spring...

    spring-boot-2.3.0.RELEASE.zip

    Spring Cloud与Spring Boot的结合,使得构建分布式微服务系统成为可能。2.3.0.RELEASE对Spring Cloud的兼容性进行了优化,如服务注册与发现、熔断器Hystrix等,进一步强化了微服务架构的稳定性和可靠性。 八、持续...

    SpringBoot+SpringCloud+Vue开发的员工管理系统源码.zip

    SpringCloud是一系列工具的集合,用于帮助开发者快速构建分布式系统中的服务发现、配置管理、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状态等。在本项目中,SpringCloud...

    基于springcloud预约上门维修服务系统.zip

    SpringCloud是基于Spring Boot的微服务解决方案,它简化了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线等)中的开发工作。在这个项目中,SpringCloud被用来拆分大型应用为多个独立的...

    Excel模板Github上10个值得学习的Springboot开发项目.pdf

    - **价值**:学习 SpringBoot 结合 Spring Cloud 构建微服务架构的方法。 8. **Roses** - **简介**:致力于提供简洁的分布式和服务化解决方案,包括分布式事务、服务治理等功能。 - **技术栈**:Spring Boot 2、...

    JavaEE开发的颠覆者 SpringBoot实战

    3. **数据库集成**:理解SpringData JPA和MyBatis等持久层框架的使用,以及如何配置数据源和事务管理。 4. **安全控制**:掌握Spring Security的基本用法,实现用户认证和授权,保护应用的安全性。 5. **测试**:...

    Spring 5源代码(spring-framework-5.3.14.tar.gz)

    尽管Spring Cloud不是Spring Framework的一部分,但它们是互补的,Spring Cloud提供了云应用开发的工具和服务发现、配置管理、断路器、智能路由、微代理、控制总线等功能,帮助开发者构建分布式系统。 通过深入...

    JavaEE开发的颠覆者 Spring Boot实战 完整版.zip

    6. **Spring Cloud集成**:Spring Boot与Spring Cloud的无缝对接,使得构建分布式系统(如配置中心、服务注册与发现、熔断机制等)变得轻而易举。 本书的实战部分会涵盖以下主题: 1. **Spring Boot入门**:讲解...

    Spring Boot 2精髓.rar

    Spring Cloud提供了服务发现、配置中心、负载均衡、熔断器等一系列微服务治理工具,如Eureka、Config Server、Zuul、Hystrix等,这些工具与Spring Boot 2的集成使得开发者可以快速搭建出高可用的分布式系统。...

    springboot+mybaties+maven聚合+eureka+ribbon访问

    在Spring Boot项目中,Maven聚合功能可以用来管理多个子模块,便于构建微服务架构。 Spring Cloud Eureka是Netflix OSS的一个组件,用于服务注册与发现。在分布式系统中,Eureka服务器充当服务注册中心,各个微服务...

Global site tag (gtag.js) - Google Analytics