当前位置:   article > 正文

Springboot-声明式事务_springboot 声明式事务

springboot 声明式事务

目录

1 Spring的事务机制

2 声名式事务

3 注解事务行为

4 类级别使用@Transactional

5 Spring Data JPA的事务支持

6 Spring Boot的事务支持

7.代码示例

1 Spring的事务机制


所有的数据访问技术都有事务处理机制,这些技术提供了API用来开启事务、提交事务来完成数据操作,或者在发生错误的时候回滚数据。
而Spring的事务机制是用统一的机制来处理不同数据访问技术的事务处理。Spring的事务机制提供了一个PlatformTransactionManager接口,不同的数据访问技术的事务使用不同的接口实现

2 声名式事务


Spring支持声名式事务,即使用注解来选择需要使用事务的方法,它使用@Transactional注解在方法上表明该方法需要事务支持。这是一个基于AOP的实现操作,使用注解式的拦截方式来理解Spring的声名式事务。被注解的方法在被调用时,Spring开启一个新的事务,当方法无异常运行结束后,Spring会提交这个事务。

在此处特别注意的是,此@Transactional注解来自org.springframework.transaction.annotation包,而不是javax.transaction。
Spring提供了一个@EnableTransactionManagement注解在配置类上来开启声名式事务的支持。使用了@EnableTransactionManagement后,Spring容器会自动扫描注解@Transactional的方法和类。

3 注解事务行为


@Transactional注解的属性来定制事务行为。

属性含义默认值
propagation

propagation定义了事务的生命周期,主要有以下选项:

REQUIRED
REQUIRED:方法A调用时没有事务新建一个事务,当在方法A调用另一个方法B的时候,方法B将使用相同的事务;如果方法B发生异常需要数据回滚的时候,整个事务数据回滚。
默认
REQUIRES_NEW:对于方法A和B,在方法调用的时候无论是否有事务都开启一个新的事务;这样如果方法B有异常不会导致方法A的数据问题
NESTED:和REQUIRES_NEW类似,但支持JDBC,不支持JPA或Hibernate
SUPPORTS:方法调用时有事务就用事务,没事务就不用事务
NOT_SUPPORTED:强制方法不在事务中执行,若有事务,在方法调用到结束阶段事务都将会被挂起
NEVER:强制方法不在事务中执行,若有事务则抛出异常
MANDATORY:强制方法在事务中执行,若无事务则抛出异常
isolation
isolation(隔离)决定了事务的完整性,处理在多事务对相同数据下的处理机制,主要包含下面的隔离级别(当然我们也不可以随意设置,这要看当前数据库是否支持)
DEFAULT
READ_UNCOMMITTED:对于在A事务里修改了一条记录但没有提交事务,在B事务可以读取到修改后的记录。可导致脏读、不可重复读以及幻读
READ_COMMITTED:只有当在A事务里修改了一条记录且提交事务之后,B事务才可以读取到提交后的记录;阻止脏读,但可能导致不可重复读和幻读
REPEATABLE_READ:不仅能实现READ_COMMITTED的功能,而且还能阻止当A事务读取了一条记录,B事务将不允许修改这条记录;阻止脏读和不可重复读,但可能出现幻读
SERIALIZABLE:次级别下事务是顺序执行的,可以避免上述级别的缺陷,但开销较大
DEFAULT:使用当前数据库的默认隔离级别,如Oracle、SQLServer是READ_COMMITTED;Mysql是REPEATABLE_READ
默认
timeout
timeout指定事务过期时间,默认为当前数据库的事务过期时间
-1,没有过期时间
readOnly
指定当前事务是否是只读事务
false
rollbackFor
指定哪个或者哪些异常可以引起事务回滚Throwable的子类
noRollbackFor
指定哪个或者哪些异常不可以引起事务回滚
Throwable的子类


4 类级别使用@Transactional


@Transactional不仅可以注解在方法上,也可以注解在类上。当注解在类上的时候意味着此类的所有public方法都是开启事务的。如果类级别和方法级别同时使用了@Transactional注解,则使用在类级别的注解会重载方法级别的注解。


5 Spring Data JPA的事务支持


Spring Data JPA对所有的默认方法都开启了事务支持,且查询类事务默认启用readOnly=true属性,这些在SimpleJpaRepository的源码中可以看到,自行去看SimpleJpaRepository的源码。

从源码我们可以看出,SimpleJpaRepository在类级别定义了@Transactional(readOnly=true),而在和save、delete相关的操作重写了@Transactional属性,此时readOnly属性是false,其余查询操作readOnly仍然为false。


6 Spring Boot的事务支持


1.自动配置的事务管理器
在使用JDBC作为数据访问技术的时候,Spring Boot为我们定义了PlatformTransactionManager的实现DataSourceTransactionManager的Bean;配置见org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration类中的定义。

在使用JPA作为数据访问技术的时候,Spring Boot为我们了定义一个PlatformTransactionManager的实现JpaTransactionManager的Bean;配置见org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration.class类中的定义:

  1. @Bean
  2. @ConditionalOnMissingBean({TransactionManager.class})
  3. public PlatformTransactionManager transactionManager(ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
  4. JpaTransactionManager transactionManager = new JpaTransactionManager();
  5. transactionManagerCustomizers.ifAvailable((customizers) -> {
  6. customizers.customize(transactionManager);
  7. });
  8. return transactionManager;
  9. }

2.自动开启注解事务的支持
Spring Boot专门用于配置事务的类为:org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,此配置类依赖于JpaBaseConfiguration和DataSourceTransactionManagerAutoConfiguration。
而在DataSourceTransactionManagerAutoConfiguration配置里还开启了对声名式事务的支持,代码如下:

  1. @Configuration(proxyBeanMethods = false)
  2. @ConditionalOnSingleCandidate(DataSource.class)
  3. static class JdbcTransactionManagerConfiguration {
  4. @Bean
  5. @ConditionalOnMissingBean(TransactionManager.class)
  6. DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource,
  7. ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
  8. DataSourceTransactionManager transactionManager = createTransactionManager(environment, dataSource);
  9. transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
  10. return transactionManager;
  11. }
  12. private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) {
  13. return environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE)
  14. ? new JdbcTransactionManager(dataSource) : new DataSourceTransactionManager(dataSource);
  15. }
  16. }

所以在Spring Boot中,无须显示开启使用@EnableTransactionManagement注解。

7.代码示例

1.引入jar包

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>org.springframework.boot</groupId>
  7. <artifactId>spring-boot-starter-data-jpa</artifactId>
  8. <!-- <version>2.6.7</version>-->
  9. </dependency>
  10. <!--引入mysql相关jar包-->
  11. <dependency>
  12. <groupId>mysql</groupId>
  13. <artifactId>mysql-connector-java</artifactId>
  14. <scope>runtime</scope>
  15. </dependency>

2.entity层

  1. package com.example.demo.entity;
  2. import lombok.AllArgsConstructor;
  3. import lombok.Data;
  4. import lombok.NoArgsConstructor;
  5. import javax.persistence.Entity;
  6. import javax.persistence.GeneratedValue;
  7. import javax.persistence.Id;
  8. /**
  9. * SearchRecordEntity
  10. * @Entity注解指明这是一个和数据库表映射的实体类
  11. * @Description
  12. */
  13. @Data
  14. @Entity
  15. @AllArgsConstructor
  16. @NoArgsConstructor
  17. public class SearchRecordEntity {
  18. /**
  19. * 主键ID
  20. * @Id注解指明这个属性映射为数据库的主键
  21. * @GeneratedValue注解默认使用主键生成方式为自增,hibernate会为我们自动生成一个名为HIBERNATE_SEQUENCE的序列
  22. */
  23. @Id
  24. @GeneratedValue
  25. private Integer id;
  26. /**
  27. * 名称
  28. */
  29. private String name;
  30. /**
  31. * 备注
  32. */
  33. private String remark;
  34. }

3.dao层

  1. package com.example.demo.dao;
  2. import com.example.demo.entity.SearchRecordEntity;
  3. import org.springframework.data.jpa.repository.JpaRepository;
  4. import org.springframework.stereotype.Repository;
  5. import java.util.List;
  6. /**
  7. * SearchRecordRepository
  8. * 定义数据访问操作的方法
  9. * @Description
  10. */
  11. @Repository
  12. public interface SearchRecordRepository extends JpaRepository<SearchRecordEntity,Integer> {
  13. }

4.service层

  1. package com.example.demo.service;
  2. import com.example.demo.entity.SearchRecordEntity;
  3. /**
  4. * SearchRecordService
  5. *
  6. * @Description
  7. */
  8. public interface SearchRecordService {
  9. /**
  10. *
  11. * @param searchRecordEntity
  12. * @return
  13. */
  14. SearchRecordEntity saveSearchRecordWithRollback(SearchRecordEntity searchRecordEntity);
  15. /**
  16. *
  17. * @param searchRecordEntity
  18. * @return
  19. */
  20. SearchRecordEntity saveSearchRecordWithoutRollback(SearchRecordEntity searchRecordEntity);
  21. }
  1. package com.example.demo.service;
  2. import com.example.demo.dao.SearchRecordRepository;
  3. import com.example.demo.entity.SearchRecordEntity;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Service;
  6. import org.springframework.transaction.annotation.Transactional;
  7. /**
  8. * SearchRecordServiceImpl
  9. *
  10. * @Description
  11. */
  12. @Service
  13. public class SearchRecordServiceImpl implements SearchRecordService {
  14. /**
  15. * 可以直接注入SearchRecordRepository的Bean
  16. */
  17. @Autowired
  18. private SearchRecordRepository searchRecordRepository;
  19. /**
  20. * 使用@Transactional注解的rollbackFor属性,指定特定异常时,数据回滚
  21. * @param searchRecordEntity
  22. * @return
  23. */
  24. @Transactional(rollbackFor = {IllegalArgumentException.class})
  25. @Override
  26. public SearchRecordEntity saveSearchRecordWithRollback(SearchRecordEntity searchRecordEntity) {
  27. SearchRecordEntity save = searchRecordRepository.save(searchRecordEntity);
  28. if(save.getName().equalsIgnoreCase("全省影视")){
  29. //手动触发异常
  30. throw new IllegalArgumentException("全省影视已存在,数据将回滚");
  31. }
  32. return save;
  33. }
  34. /**
  35. * 使用@Transactional注解的noRollbackFor属性,指定特定异常时,数据回滚
  36. * @param searchRecordEntity
  37. * @return
  38. */
  39. @Transactional(noRollbackFor ={IllegalArgumentException.class} )
  40. @Override
  41. public SearchRecordEntity saveSearchRecordWithoutRollback(SearchRecordEntity searchRecordEntity) {
  42. SearchRecordEntity save = searchRecordRepository.save(searchRecordEntity);
  43. if(save.getName().equalsIgnoreCase("全省影视")){
  44. throw new IllegalArgumentException("全省影视已存在,数据将回滚");
  45. }
  46. return save;
  47. }
  48. }

5.controller层

  1. package com.example.demo.controller;
  2. import com.example.demo.entity.SearchRecordEntity;
  3. import com.example.demo.service.SearchRecordService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. /**
  8. * SearchRecordController1
  9. *
  10. * @Description
  11. */
  12. @RestController
  13. @RequestMapping("/api/search")
  14. public class SearchRecordController {
  15. @Autowired
  16. private SearchRecordService searchRecordService;
  17. @RequestMapping("/rollback")
  18. public SearchRecordEntity saveSearchRecordWithRollback(SearchRecordEntity searchRecordEntity) {
  19. SearchRecordEntity save = searchRecordService.saveSearchRecordWithRollback(searchRecordEntity);
  20. return save;
  21. }
  22. @RequestMapping("/noRollback")
  23. public SearchRecordEntity saveSearchRecordWithoutRollback(SearchRecordEntity searchRecordEntity) {
  24. SearchRecordEntity save = searchRecordService.saveSearchRecordWithoutRollback(searchRecordEntity);
  25. return save;
  26. }
  27. }

6.启动类开启事务管理

  1. package com.example.demo;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  5. import org.springframework.transaction.annotation.EnableTransactionManagement;
  6. @SpringBootApplication
  7. @EnableJpaRepositories(value = "com.example.demo.dao")
  8. @EnableTransactionManagement
  9. public class DemoApplication {
  10. public static void main(String[] args) {
  11. SpringApplication.run(DemoApplication.class, args);
  12. }
  13. }

7.测试

现有数据

(1)测试不回滚

访问:http://localhost:8080/api/search/noRollback?name=全省影视&remark=备注呵呵哈哈哈

 查询现有数据,发现已保存。

 (2)测试回滚

访问:http://localhost:8080/api/search/rollback?name=全省影视&remark=备注哈哈哈

 

 查询数据库,没有新增数据。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号