【SpringBoot深入浅出】5 - SpringBoot的数据库方面

8 配置数据源

8.1 使用Spring默认的数据源

在Maven添加如下依赖

        <!-- jdbc启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- web启动器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- mysql连接依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
  1. resources/下新建application.yaml,配置数据库连接要素
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_schema?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  1. 在测试类中进行测试
package com.cap.dbdemo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.sql.DataSource;

@SpringBootTest
class DbdemoApplicationTests {
    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() {
        System.out.println("数据源为:"+dataSource);
    }
}
  1. 运行结果如下:
    在这里插入图片描述
    可以看到,Spring的默认数据源为HikariDataSource
    我们打开spring-boot-starter-jdbc启动器
    在这里插入图片描述在spring-boot-starter-jdbc启动器中,确实使用了HikariCP作为默认的数据库连接池
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20200723143731540.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0NhcDIyMDU5MA==,size_16,color_FFFFFF,t_70

HikariCP连接池
Hikari,希卡利,日语中意为“光芒”
HikariCP连接池的官网是https://github.com/brettwooldridge/HikariCP
HikariCP连接池作为一个后起之秀,其优点主要有:

  • 速度快,执行效率高
  • bug少,因其自身代码量少
  • 稳定

可以说HikariCP连接池是目前速度最快的连接池,springboot2.0其就将其作为默认的数据库连接池

既然我们知道了SpringBoot将HikariCP作为默认的数据库连接池,那么我们就可以来配置一下HikariCP连接池的一些属性:

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_schema?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: root
    ## type可以显式地将数据源设置为HikariDataSource
    ## 这里我们也可以不设置,毕竟默认就是HikariDataSource
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      ## 连接池名字
      pool-name: myHikariCP
      ## 最小空闲连接数量
      minimum-idle: 10
      ## 空闲连接存活最大时间,默认为600000毫秒(10分钟)
      idle-timeout: 600000
      ## 连接池最大连接数量,默认是10
      maximum-pool-size: 10
      ## 设置返回的连接自动提交事务,默认为true
      auto-commit: true
      ## 单个连接最大的连接时间,0表示无生命期限,默认为1800000毫秒(30分钟)
      max-lifetime: 1800000
      ## 连接超时时间,默认为30000毫秒(30秒)
      connection-timeout: 30000

注意我们在配置文件中使用type属性可以显式设置数据源,这对我们下一节来配置自定义数据源非常有用

8.2 配置自定义数据源——Druid

  1. pom.xml中导入Druid启动器
        <!-- Druid连接池启动器 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>
  1. 修改application.yaml
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_schema?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: mysql
    ## 将数据源设置为DruidDataSource
    type: com.alibaba.druid.pool.DruidDataSource
  1. 修改测试方法
package com.cap.dbdemo;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.sql.DataSource;

@SpringBootTest
class DbdemoApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() {
        System.out.println("数据源为:"+dataSource.getClass());
    }

}
  1. 测试结果为:
    在这里插入图片描述

在测试过程中,我们是让SpringBoot为我们自动生成数据源,接下来我们将自己绑定自定义的数据源,并将其放入IOC容器中

package com.cap.dbdemo.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
public class DruidDataSource {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public static DataSource getDruidDataSource(){
        return new com.alibaba.druid.pool.DruidDataSource();
    }
}

修改测试方法

package com.cap.dbdemo;

import com.alibaba.druid.pool.DruidDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@SpringBootTest
class DbdemoApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
        System.out.println("数据源为:"+dataSource.getClass());

        System.out.println(dataSource.getConnection());

        DruidDataSource druidDataSource = (DruidDataSource) dataSource;
        System.out.println("最大的连接数为:"+druidDataSource.getMaxActive());
        System.out.println("数据源初始化连接数:"+druidDataSource.getInitialSize());

    }

}

结果得到
在这里插入图片描述

8.3 开启Druid的后台监控功能方式一

Druid比HikariCP优势之处在于Druid有强大的监控功能,下面我们来开启此功能

  1. 配置ServletRegistrationBean,可以注册Servlet,向其提供StatViewServlet,该Servlet可以配置后台监控供功能
package com.cap.dbdemo.config;

import com.alibaba.druid.support.http.StatViewServlet;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.HashMap;

@Configuration
public class DruidDataSource {

    //这是Druid提供的一个可以进行后台监控的Servlet
    //由于没有web.xml,我们使用SpringBoot注册Servlet方式
    @Bean
    public ServletRegistrationBean statViewServlet(){
        ServletRegistrationBean<StatViewServlet> bean =
                new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
        HashMap<String, String> initParameters = new HashMap<>();

        //设置登录账号和密码
        initParameters.put("loginUsername","admin");
        initParameters.put("loginPassword","123456");
        //设置允许访问的路径
        //为空时表示无限制
        //为localhost表示只能本地访问
        initParameters.put("allow","");

        bean.setInitParameters(initParameters);
        return bean;
    }

}
  1. ServletRegistrationBean是Druid提供的一个可以进行后台监控的Servlet,由于没有web.xml,我们使用SpringBoot注册Servlet方式
  2. StatViewServlet的参数,比如loginUsernameloginPassword等这些参数可以在 com.alibaba.druid.support.http.StatViewServlet的父类 com.alibaba.druid.support.http.ResourceServlet中找到
  1. 在浏览器上输入localhost:8080/druid即可访问监控后台
    在这里插入图片描述
    使用我们设置的loginUsername账号和loginPassword密码登录进去
    在这里插入图片描述
    可以看到,Druid为我们提供了多项后台监控的选项,有数据源、SQL监控等等等等

8.4 开启Druid的后台监控功能方式二

我们也可以通过配置文件来开启Druid后台监控功能
application.yaml中添加stat-view-servlet

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_schema?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: mysql
    ## 将数据源设置为DruidDataSource
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      stat-view-servlet:
        login-username: master
        login-password: 123456
        url-pattern: /druid/*
        # allow:
        # deny:
        # 表示开启后台监控功能
        enabled: true

注意:stat-view-servlet的enabled属性必须为true才会开启后台监控功能

在这里插入图片描述
在这里插入图片描述
补充,使用配置文件,我们也可以轻松地为后台监控配置过滤规则

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot_schema?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: mysql
    ## 将数据源设置为DruidDataSource
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      stat-view-servlet:
        login-username: master
        login-password: 123456
        url-pattern: /druid/*
        # allow:
        # deny:
        # 表示开启后台监控功能
        enabled: true
      web-stat-filter:
        # 添加过滤规则
        url-pattern: /*
        # 忽略过滤格式
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
        # 表示开启后台监控过滤功能
        enabled: true

9 SpringBoot整合MyBatis

整个工程结构如下:
在这里插入图片描述在这里插入图片描述

9.1 创建测试数据库和数据表

mysql> CREATE DATABASE sm_schema;
Query OK, 1 row affected (0.03 sec)
mysql> USE sm_schema;
Database changed
mysql> CREATE TABLE t_user(
    -> id INT NOT NULL AUTO_INCREMENT,
    -> user_name VARCHAR(60) NOT NULL,
    -> sex INT NOT NULL DEFAULT 1 check (sex in (1, 2)),
    -> note VARCHAR(256) NULL,
    -> PRIMARY KEY(id)
    -> )ENGINE = InnoDB, CHARSET = UTF8;
Query OK, 0 rows affected, 1 warning (0.11 sec)
mysql> INSERT INTO t_user(user_name,sex,note)
    -> VALUES
    -> ("张三",1,"深圳高中"),
    -> ("李四",2,"唱跳rap"),
    -> ("王五",1,"吃饱了撑着");
Query OK, 3 rows affected (0.01 sec)
Records: 3  Duplicates: 0  Warnings: 0
mysql> SELECT * FROM t_user;
+----+-----------+-----+-----------------+
| id | user_name | sex | note            |
+----+-----------+-----+-----------------+
|  1 | 张三      |   1 | 深圳高中        |
|  2 | 李四      |   2 | 唱跳rap         |
|  3 | 王五      |   1 | 吃饱了撑着      |
+----+-----------+-----+-----------------+
3 rows in set (0.00 sec)

9.2 创建Druid数据源

  1. 导入Druid启动器
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>
  1. 在application.yaml配置数据源
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/sm_schema?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
    username: root
    password: mysql
    ## 将数据源设置为DruidDataSource
    type: com.alibaba.druid.pool.DruidDataSource

9.3 创建实体类User

package com.cap.springbootmybatis.pojo;

import com.cap.springbootmybatis.enumeration.SexEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.type.Alias;

/**
 * @author cap
 * @create 2020.07.25.20:40
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Alias("user")
public class User {
    private Long id;
    private String userName;
    private SexEnum sex;
    private String note;
}

9.4 创建枚举类SexEnum和对应的类型处理器SexTypeHandler

package com.cap.springbootmybatis.enumeration;

/**
 * @author cap
 * @create 2020.07.25.20:41
 */

public enum SexEnum {
    MALE(1,"男"),
    FEMALE(2,"女");

    private int id;
    private String name;

    SexEnum(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static SexEnum getEnumById(int id){
        for (SexEnum sex : SexEnum.values()) {
            if(sex.getId() == id){
                return sex;
            }
        }
        return null;
    }

    public static boolean isSexEnum(int id){
        return id == 1 || id == 2 ? true : false;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
package com.cap.springbootmybatis.typehandler;

import com.cap.springbootmybatis.enumeration.SexEnum;
import org.apache.ibatis.javassist.bytecode.SignatureAttribute;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @author cap
 * @create 2020.07.25.21:04
 */
@MappedJdbcTypes(JdbcType.INTEGER)
@MappedTypes(SexEnum.class)
public class SexTypeHandler extends BaseTypeHandler<SexEnum> {

    //设置非空性别参数:将实体类数据转化为数据库表的对应数据
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, SexEnum parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i,parameter.getId());
    }

    //将查询到数据库表的数据转化为实体类对应的数据
    //通过列名读取性别
    @Override
    public SexEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int sex = rs.getInt(columnName);
        return SexEnum.isSexEnum(sex) ? SexEnum.getEnumById(sex) : null;
    }

    //通过下标读取性别
    @Override
    public SexEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int sex = rs.getInt(columnIndex);
        return SexEnum.isSexEnum(sex) ? SexEnum.getEnumById(sex) : null;
    }

    //通过存储过程读取性别
    @Override
    public SexEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int sex = cs.getInt(columnIndex);
        return SexEnum.isSexEnum(sex) ? SexEnum.getEnumById(sex) : null;
    }
}

9.5 创建映射接口UserMapper

package com.cap.springbootmybatis.mapper;

import com.cap.springbootmybatis.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

/**
 * @author cap
 * @create 2020.07.25.20:46
 */
@Repository
@Mapper
public interface UserMapper {
    User getUserById(@Param("id")long id);
}

9.6 创建映射文件UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.cap.springbootmybatis.mapper.UserMapper">
    <select id="getUserById" resultMap="getUserResultMap">
        SELECT id, user_name, sex, note
        FROM t_user
        WHERE id = #{id};
    </select>
    <resultMap id="getUserResultMap" type="user">
        <id property="id" column="id" />
        <result property="userName" column="user_name" />
        <result property="sex" column="sex" />
        <result property="note" column="note" />
    </resultMap>
</mapper>

9.7 处理别名和类型处理器的配置类MyBatisConfig

package com.cap.springbootmybatis.mybatisconfig;

import com.cap.springbootmybatis.pojo.User;
import com.cap.springbootmybatis.typehandler.SexTypeHandler;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * @author cap
 * @create 2020.07.25.22:52
 */
@Configuration
public class MyBatisConfig {
    @Autowired
    DataSource dataSource;

    @ConfigurationProperties("mybatis")
    @Bean
    public SqlSessionFactory masterSqlSessionFactory() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setVfs(SpringBootVFS.class);
        factoryBean.setTypeAliases(User.class);
        factoryBean.setTypeHandlers(new SexTypeHandler());
        factoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResource("classpath:com/cap/springbootmybatis/mapper/UserMapper.xml"));
        return factoryBean.getObject();
    }
}

9.8 配置mybatis

在application.yaml配置mybatis

mybatis:
  mapper-locations: classpath:com/cap/springbootmybatis/mapper/*.xml

9.10 测试

package com.cap.springbootmybatis;

import com.cap.springbootmybatis.mapper.UserMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootMybatisApplication implements CommandLineRunner {
    private final UserMapper userMapper;

    public SpringbootMybatisApplication(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringbootMybatisApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println(userMapper.getUserById(1));
    }
}

结果得到

User(id=1, userName=张三, sex=MALE, note=深圳高中)

10 声明式事务

10.1 修改UserMapper接口,增加addUser方法

package com.cap.springbootmybatis.mapper;

import com.cap.springbootmybatis.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

/**
 * @author cap
 * @create 2020.07.25.20:46
 */
@Repository
@Mapper
public interface UserMapper {

    User getUserById(@Param("id")long id);

    int addUser(User user);
}

10.2 增加一个service层的接口UserService

package com.cap.springbootmybatis.service;

import com.cap.springbootmybatis.pojo.User;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Service;

/**
 * @author cap
 * @create 2020.07.26.16:10
 */
public interface UserService {

    User getUserById(long id);

    int addUser(User user);

}

10.3 增加一个实现类UserServiceImpl

package com.cap.springbootmybatis.service.Impl;

import com.cap.springbootmybatis.mapper.UserMapper;
import com.cap.springbootmybatis.pojo.User;
import com.cap.springbootmybatis.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author cap
 * @create 2020.07.26.16:11
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, timeout = 1)
    public User getUserById(long id) {
        return userMapper.getUserById(id);
    }

    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, timeout = 1)
    public int addUser(User user) {
        return userMapper.addUser(user);
    }
}

10.4 测试

package com.cap.springbootmybatis;

import com.cap.springbootmybatis.enumeration.SexEnum;
import com.cap.springbootmybatis.pojo.User;
import com.cap.springbootmybatis.service.UserService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootMybatisApplication implements CommandLineRunner {
    private final UserService userService;

    public SpringbootMybatisApplication(UserService userService) {
        this.userService = userService;
    }

    public static void main(String[] args) {
        SpringApplication.run(SpringbootMybatisApplication.class, args);
    }
    
    public void run(String... args) throws Exception {
        User user = new User();
        user.setUserName("宋江");
        user.setSex(SexEnum.MALE);
        user.setNote("人送外号“及时雨”");
        System.out.println("插入前的user:"+user);
        System.out.println("返回结果为"+userService.addUser(user));
        System.out.println("插入后的user:"+user);
    }
}

结果得到:

插入前的user:User(id=null, userName=宋江, sex=MALE, note=人送外号“及时雨”)
返回结果为1
插入后的user:User(id=8, userName=宋江, sex=MALE, note=人送外号“及时雨”)

10.5 详解@Transaction注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
	//通过bean name指定事务管理器
	@AliasFor("transactionManager")
	String value() default "";
	//同value属性
	@AliasFor("value")
	String transactionManager() default "";

	//指定传播行为
	Propagation propagation() default Propagation.REQUIRED;

	//指定隔离级别
	Isolation isolation() default Isolation.DEFAULT;

	//指定超时时间(单位:秒)
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	//指定是否只读事务
	boolean readOnly() default false;
	
	// 方法在发生指定异常时回滚,默认是所有异常都回滚
	Class<? extends Throwable>[] rollbackFor() default {};

	// 方法在发生指定异常名称时回滚,默认是所有异常都回滚
	String[] rollbackForClassName() default {};

	// 方法在发生指定异常时不回滚,默认是所有异常都回滚
	Class<? extends Throwable>[] noRollbackFor() default {};

	// 方法在发生指定异常时不回滚,默认是所有异常都回滚
	String[] noRollbackForClassName() default {};

}

10.6 事务管理器

事务的打开、回滚和提交都是事务管理器来完成的。
在这里插入图片描述事务管理器的顶层接口是PlatformTransactionManager,最常用的事务处管理器是DataSourceTransactionManager

当你使用mybatis启动器时,Spring会提供DataSourceTransactionManager对象作为事务管理器,当你使用jpa启动器时,spring会提供JpaTransactionManager对象作为事务管理器,所以一般我们不需要自己创建事务管理器,直接使用即可。

下面是PlatformTransactionManager的源码,Spring在进行事务管理的时候,会将下面这些方法织入对应的流程中。

public interface PlatformTransactionManager extends TransactionManager {

	//获取事务,设置数据属性
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;
	//提交事务
	void commit(TransactionStatus status) throws TransactionException;
	
	//回滚事务
	void rollback(TransactionStatus status) throws TransactionException;

}

测试事务管理器

package com.cap.springbootmybatis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.PlatformTransactionManager;

import javax.annotation.PostConstruct;

@SpringBootApplication
public class SpringbootMybatisApplication {

    private final UserService userService;

    @Autowired
    PlatformTransactionManager txManager = null;

    public SpringbootMybatisApplication(UserService userService) {
        this.userService = userService;
    }


    public static void main(String[] args) {
        SpringApplication.run(SpringbootMybatisApplication.class, args);
    }

    @PostConstruct
    public void viewTxManager(){
        System.out.println(txManager.getClass().getName());
    }

}

@PostConstruct注解会在类对象初始化后调用注解的方法

输出结果为:

org.springframework.jdbc.datasource.DataSourceTransactionManager

可以看到,我们使用的mybatis框架,Spring确实为我们提供了DataSourceTransactionManager事务管理器

当我们在System.out.println(txManager.getClass().getName());上加入断点观察,可以得到
在这里插入图片描述

11 传播行为

  • 传播行为 是指当方法互相调用,方法中的事务如何进行的策略

在这里插入图片描述

  • 传播行为的分类:
    • REQUIRED,需要事务,默认的传播行为,如果当前存在事务,就沿用当前事务,如果没有当前不存在事务,那就新建一个事务来运行子方法。
    • SUPPORTS:支持事务,如果当前存在事务,就沿用当前事务,如果当前事务不存在,则继续采用无事务的方式运行子方法。
    • MANDATORY:强制事务,如果当前没有事务,则会抛出异常,如果当前事务存在,就沿用当前事务
    • REQUIRES_NEW:需要新事务,无论当前事务是否存在,都会创建新事务运行方法,这样新事务就可以拥有新的所和隔离级别等特性,与当前事务相互独立。
    • NOT_SUPPORTED:不支持事务,将挂起事务,运行方法
    • NEVER:不需要事务:如果当前方法存在事务,则抛出异常,否则继续使用无事务机制运行。
    • NESTED:内嵌事务,当前方法调用子方法时,如果子方法发生异常,只回滚子方法执行过的SQL,不回滚当前方法的事物。

REQUIRES_NEW和REQUIRES_NEW的区别:

  • REQUIRES_NEW:需要新事务会为子方法新建一个新的事务,这意味着子方法会有自己独立的事务和锁
  • NESTED:对于内嵌事务子方法仍然沿用当前事务,当子方法的SQL发生异常时,会使用保存点(save point)来进行回滚(当使用的数据库不支持保存点技术时,spring就采用REQUIRES_NEW为子方法新建一个事务)

猜你喜欢

转载自blog.csdn.net/Cap220590/article/details/107536047
今日推荐