文章目录
- 8 配置数据源
- 9 SpringBoot整合MyBatis
- 9.1 创建测试数据库和数据表
- 9.2 创建Druid数据源
- 9.3 创建实体类User
- 9.4 创建枚举类SexEnum和对应的类型处理器SexTypeHandler
- 9.5 创建映射接口UserMapper
- 9.6 创建映射文件UserMapper.xml
- 9.7 处理别名和类型处理器的配置类MyBatisConfig
- 9.8 配置mybatis
- 9.10 测试
- 10 声明式事务
- 10.1 修改UserMapper接口,增加addUser方法
- 10.2 增加一个service层的接口UserService
- 10.3 增加一个实现类UserServiceImpl
- 10.4 测试
- 10.5 详解
@Transaction
注解 - 10.6 事务管理器
- 11 传播行为
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>
- 在
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
- 在测试类中进行测试
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);
}
}
- 运行结果如下:
可以看到,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
- 在
pom.xml
中导入Druid启动器
<!-- Druid连接池启动器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
- 修改
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
- 修改测试方法
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());
}
}
- 测试结果为:
在测试过程中,我们是让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有强大的监控功能,下面我们来开启此功能
- 配置
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;
}
}
ServletRegistrationBean
是Druid提供的一个可以进行后台监控的Servlet
,由于没有web.xml
,我们使用SpringBoot注册Servlet
方式StatViewServlet
的参数,比如loginUsername
,loginPassword
等这些参数可以在com.alibaba.druid.support.http.StatViewServlet
的父类com.alibaba.druid.support.http.ResourceServlet
中找到
- 在浏览器上输入
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数据源
- 导入Druid启动器
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
- 在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为子方法新建一个事务)