8、SpringBoot 事务

系列导航

springBoot项目打jar包

1、springboot工程新建(单模块)

2、springboot创建多模块工程

3、springboot连接数据库

4、SpringBoot连接数据库引入druid

5、SpringBoot连接数据库引入mybatis

6、SpringBoot-mybatis分页实现pagehelper

7、SpringBoot-mybatis-plus引入

8、SpringBoot 事务

9、SpringBoot-mybatis-druid多源数据多源数据

10、SpringBoot-mybatis-plus-druid多源数据

11、SpringBoot-mybatis-plus-druid多源数据事务

12、SpringBoot-mybatis-plus-ehcache

13、SpringBoot-配置文件里密码加密

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中执行

 

代码报主键冲突,数据库一条数据也没有,说明事务生效

 

 

 

其他几个测试差不多就不重复了。自己看代码实验,代码里注释还比较清晰

 

posted @ 2022-04-13 18:04  万笑佛  阅读(204)  评论(2编辑  收藏  举报