8、SpringBoot 事务
系列导航
6、SpringBoot-mybatis分页实现pagehelper
9、SpringBoot-mybatis-druid多源数据多源数据
10、SpringBoot-mybatis-plus-druid多源数据
11、SpringBoot-mybatis-plus-druid多源数据事务
12、SpringBoot-mybatis-plus-ehcache
14、SpringBoot-easyexcel导出excle
完结
关于springBoot中的事务如何出里这里做了一些实验分享给大家
1数据库中创建表
oracle脚本
CREATE TABLE TEST_BLOCK_T ( BLOCK_ID VARCHAR2(10 BYTE) PRIMARY KEY, --编码 BLOCK_NAME VARCHAR2(200 BYTE) --资源名称 );
2、pom.xml文件
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-boot.version>2.1.17.RELEASE</spring-boot.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- oracle驱动 --> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.3</version> </dependency> <!-- 集成druid --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.10</version> </dependency> <!-- 集成mybatis-plus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>${spring-boot.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <version>2.1.17.RELEASE</version> <configuration> <mainClass>com.example.demo.DemoApplication</mainClass> </configuration> <executions> <execution> <id>repackage</id> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
3、 application.properties配置
# 应用名称
spring.application.name=demo
# 应用服务 WEB 访问端口
server.port=8080
# 数据库设置
spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@192.168.0.100:1521:orcl
spring.datasource.username=zy
spring.datasource.password=1
# druid配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# druid参数调优(可选)
# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
# 测试连接
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 打开PSCache,并且指定每个连接上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置监控统计拦截的filters
spring.datasource.filters=stat
# asyncInit是1.1.4中新增加的配置,如果有initialSize数量较多时,打开会加快应用启动时间
spring.datasource.asyncInit=true
#mybatis-plus控制台打印sql
mybatis-plus.configuration.log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
4、文件目录
5、源码
启动类
注意要开启事务@EnableTransactionManagement
package com.example.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @EnableTransactionManagement @MapperScan("com.example.demo.mapper") public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
数据连接配置类
package com.example.demo.config; import com.alibaba.druid.pool.DruidDataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import javax.sql.DataSource; import java.sql.SQLException; @Configuration public class DruidConfig { private Logger logger = LoggerFactory.getLogger(DruidConfig.class); @Value("${spring.datasource.url}") private String dbUrl; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Value("${spring.datasource.driver-class-name}") private String driverClassName; @Value("${spring.datasource.initial-size}") private int initialSize; @Value("${spring.datasource.min-idle}") private int minIdle; @Value("${spring.datasource.max-active}") private int maxActive; @Value("${spring.datasource.max-wait}") private int maxWait; @Value("${spring.datasource.time-between-eviction-runs-millis}") private int timeBetweenEvictionRunsMillis; @Value("${spring.datasource.min-evictable-idle-time-millis}") private int minEvictableIdleTimeMillis; // @Value("${spring.datasource.validation-query}") // private String validationQuery; @Value("${spring.datasource.test-while-idle}") private boolean testWhileIdle; @Value("${spring.datasource.test-on-borrow}") private boolean testOnBorrow; @Value("${spring.datasource.test-on-return}") private boolean testOnReturn; @Value("${spring.datasource.pool-prepared-statements}") private boolean poolPreparedStatements; @Value("${spring.datasource.max-pool-prepared-statement-per-connection-size}") private int maxPoolPreparedStatementPerConnectionSize; @Value("${spring.datasource.filters}") private String filters; @Bean //声明其为Bean实例 @Primary //在同样的DataSource中,首先使用被标注的DataSource public DataSource dataSource(){ DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(this.dbUrl); datasource.setUsername(username); datasource.setPassword(password); datasource.setDriverClassName(driverClassName); //configuration datasource.setInitialSize(initialSize); datasource.setMinIdle(minIdle); datasource.setMaxActive(maxActive); datasource.setMaxWait(maxWait); datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); // datasource.setValidationQuery(validationQuery); datasource.setTestWhileIdle(testWhileIdle); datasource.setTestOnBorrow(testOnBorrow); datasource.setTestOnReturn(testOnReturn); datasource.setPoolPreparedStatements(poolPreparedStatements); datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); try { datasource.setFilters(filters); } catch (SQLException e) { logger.error("druid configuration initialization filter", e); } // datasource.setConnectionProperties(connectionProperties); return datasource; } }
controller类
package com.example.demo.controller; import com.example.demo.service.TransActionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/hello") public class TransActionController { @Autowired TransActionService transActionService; @GetMapping("/test1") @ResponseBody public String test1() { transActionService.test1(); return "success"; } @GetMapping("/test2") @ResponseBody public String test2() { transActionService.test2(); return "success"; } @GetMapping("/test3") @ResponseBody public String test3() { transActionService.test3(); return "success"; } @GetMapping("/test3_1") @ResponseBody public String test3_1() { transActionService.test3_1(); return "success"; } @GetMapping("/test4") @ResponseBody public String test4() { transActionService.test4(); return "success"; } }
实体类
package com.example.demo.domain; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; /** * <p> * 。 * </p> * * @author yc * @since 2021-09-18 */ @TableName(value = "TEST_BLOCK_T") public class Block { private static final long serialVersionUID = 1L; @TableId private String blockId; /** * $field.comment。 */ private String blockName; public String getBlockId() { return blockId; } public void setBlockId(String blockId) { this.blockId = blockId; } public String getBlockName() { return blockName; } public void setBlockName(String blockName) { this.blockName = blockName; } @Override public String toString() { return "XyDicBlockT{" + "blockId='" + blockId + '\'' + ", blockName='" + blockName + '\'' + '}'; } }
mapper类
package com.example.demo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.demo.domain.Block; public interface BlockMapper extends BaseMapper<Block> { }
service类
package com.example.demo.service; import com.example.demo.domain.Block; import com.example.demo.mapper.BlockMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; import java.sql.SQLException; @Service public class TransActionService { //SpringBoot在使用事物Transactional的时候,要在main方法上加上 @EnableTransactionManagement // 注解开发事物声明,在使用的service层的公共方法加上 @Transactional (spring)注解。 @Autowired BlockMapper BlockMapper; @Autowired private DataSourceTransactionManager dataSourceTransactionManager; @Autowired private TransactionDefinition transactionDefinition; //那么首先我们来看下 @Transactional 这个注解的使用方法吧,只需要你在需要添加公共方法上面添加该注解即可。 // 但是这么使用的话需要你将异常抛出,由spring进行去控制。 @Transactional public void test1() { Block block1 = new Block(); block1.setBlockId("1"); BlockMapper.insert(block1); Block block2 = new Block(); block2.setBlockId("1"); BlockMapper.insert(block2); } //如果我们在使用事物 @Transactional 的时候,想自己对异常进行处理的话,那么我们可以进行手动回滚事物。 // 在catch中加上 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); // 方法进行手动回滚。不过需要注意的是发生异常需要第一时间进行手动回滚事物,也就是要在异常抛出之前! @Transactional public boolean test2() { try { Block block1 = new Block(); block1.setBlockId("1"); BlockMapper.insert(block1); Block block2 = new Block(); block2.setBlockId("1"); BlockMapper.insert(block2); } catch (Exception e) { System.out.println("发生异常,进行手动回滚!"); // 手动回滚事物 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); e.printStackTrace(); } return false; } //如果我们在使用事物 @Transactional 的时候,调用了其他的子方法进行了数据库的操作, //但是我们想使其事物生效的话,我们可以使用rollbackFor注解或者将该子方法的异常抛出由调用的方法进行处理, //不过这里需要注意的是,子方法也必须是公共的方法! @Transactional public boolean test3() { try { deal1(); deal2(); } catch (Exception e) { System.out.println("发生异常,进行手动回滚!"); // 手动回滚事物 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); e.printStackTrace(); } return false; } public void deal1() throws SQLException { Block block = new Block(); block.setBlockId("1"); BlockMapper.insert(block); System.out.println("执行方法deal1:"); } @Transactional(rollbackFor = SQLException.class) public void deal2() { Block block = new Block(); block.setBlockId("1"); BlockMapper.insert(block); System.out.println("执行方法deal2:"); } //这样写经过测试事务也是生效的。好像子方法里不加throws SQLException 和 @Transactional(rollbackFor = SQLException.class) 事务也是生效的 @Transactional public boolean test3_1() { deal1_1(); deal2_1(); return false; } public void deal1_1() { Block block = new Block(); block.setBlockId("1"); BlockMapper.insert(block); System.out.println("执行方法deal1_1:"); } public void deal2_1() { Block block = new Block(); block.setBlockId("1"); BlockMapper.insert(block); System.out.println("执行方法deal2_1:"); } //----------------------------------- // 如果我们不想使用事物 @Transactional 注解,想自己进行事物控制(编程事物管理),控制某一段的代码事物生效, // 但是又不想自己去编写那么多的代码,那么可以使用springboot中的DataSourceTransactionManager和TransactionDefinition这两个类来结合使用, // 能够达到手动控制事物的提交回滚。不过在进行使用的时候,需要注意在回滚的时候,要确保开启了事物但是未提交, // 如果未开启或已提交的时候进行回滚是会在catch里面发生异常的! public boolean test4() { /* * 手动进行事物控制 */ TransactionStatus transactionStatus=null; boolean isCommit = false; try { transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition); // 进行新增/修改 Block block1 = new Block(); block1.setBlockId("1"); BlockMapper.insert(block1); Block block2 = new Block(); block2.setBlockId("1"); BlockMapper.insert(block2); //手动提交 dataSourceTransactionManager.commit(transactionStatus); isCommit= true; System.out.println("手动提交事物成功!"); } catch (Exception e) { //如果未提交就进行回滚 if(!isCommit){ System.out.println("发生异常,进行手动回滚!"); //手动回滚事物 dataSourceTransactionManager.rollback(transactionStatus); } e.printStackTrace(); } return false; } }
6、启动项目测试项目
1、测试1-1
(1)TEST_BLOCK_T中数据清空
(2)TransActionService中test1()注销掉@Transactional
//@Transactional
public void test1()
postman中执行
代码报主键冲突,但是数据库里有一条数据,说明该方法没有事务
2、测试1-2
(1)TEST_BLOCK_T中数据清空
(2)TransActionService中test1()增加@Transactional
@Transactional
public void test1()
postman中执行
代码报主键冲突,数据库一条数据也没有,说明事务生效
其他几个测试差不多就不重复了。自己看代码实验,代码里注释还比较清晰