JDBC的数据库事务

传统JDBC的数据库事务的一个示例如下代码所示,该示例仅为一个insertUser方法的数据库事务过程。可以看到,如果还存在很多其他的数据库事务需要,则需要编写很多类似于如下的代码过程,而其中大部分过程是重复的,仅为SQL的执行过程不相同。

@Service
public class JdbcServiceImpl implements JdbcService {@Autowiredprivate DataSource dataSource;@Overridepublic int insertUser(User user) {Connection connection = null;int result = 0;try {// 创建数据库连接connection = dataSource.getConnection();// 设置是否自动提交connection.setAutoCommit(false);// 开启事务并设置隔离级别connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);PreparedStatement statement = connection.prepareStatement("insert into t_user (user_name, sex, note) values (?, ?, ?)");statement.setString(1, user.getUserName());statement.setInt(2, user.getSex().getId());statement.setString(3, user.getNote());// 执行SQLresult = statement.executeUpdate();// 提交事务connection.commit();} catch (SQLException e) {// 发生异常时回滚事务if (connection != null) {try {connection.rollback();} catch (SQLException ex) {ex.printStackTrace();}}e.printStackTrace();} finally {// 关闭数据库连接try {if (connection != null || !connection.isClosed()) {connection.close();}} catch (SQLException e) {e.printStackTrace();}}return result;}
}

而上述过程可以抽象为如下的数据库事务执行过程:

针对这种数据库事务的使用问题,Spring中使用AOP的方式将上述约定流程中除SQL执行以外的过程抽取出来单独实现,并将用户编写的SQL执行代码织入流程,从而简化大量公共代码。

Spring声明式事务

在Spring中,数据库事务主要使用声明式配置的方式,对于需要开启事务的类或者方法使用@Transactional注解进行标注。当其标注在类上时,表明这个类的所有publid、非静态方法都将启动事务,当标注在方法上时,则是对当前方法使用数据库事务。

此外@Transactional注解中还可以配置多个属性,例如事务的隔离级别以及转播行为等。这些配置内容,在Spring IOC容器加载时进行解析,然后将这些信息存到事务定义器(TransactionDefinition)中,并且记录那些类或者方法需要启动数据库事务,采取什么策略执行事务。此时在Spring中的事务处理过程即为如下:

所以,在开始数据库事务时,现在仅需要使用@Transactional注解进行标注,Spring会自动完成数据库连接、提交、回滚以及资源的释放等过程。

@Transactional注解的配置项

Spring中的数据库的事务属性通过@Transactional注解的属性进行配置,下面可以从该注解的源码中看到相关的配置项:

@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;// 指定事务超时时间(单位s)int timeout() default -1;// 是否为只读事务boolean readOnly() default false;// 方法在发生指定异常时回滚,默认是所有异常都回滚Class<? extends Throwable>[] rollbackFor() default {};// 方法在发生指定异常名称时回滚,默认是所有异常都回滚String[] rollbackForClassName() default {};// 方法在发生指定异常时不回滚,默认是所有异常都回滚Class<? extends Throwable>[] noRollbackFor() default {};// 方法在发生指定异常名称时不回滚,默认是所有异常都回滚String[] noRollbackForClassName() default {};
}

其中,isolation和propagation将在后续进行说明,而rollbackFor, rollbackForClassName, noRollbackFor, noRollbackForClassName都是指定异常,也就是指定在什么异常发生的情况下依旧提交事务,在什么异常的情况下回滚事务。

数据库事务管理器

在上述数据库事务的流程中,事务的打开,回滚和提交都是由事务管理器来完成的,在Spring中事务管理器的顶层接口为PlatformTransactionManager,并存在多个相关的实现类和接口。例如引入Hibernate是Spring ORM包会提供HibernateTransactionManager,引入MyBatis包时,使用的管理器为DataSourceTransactionManager(这里使用的为MyBatis)。

在Spring Boot中,当依赖于mybatis-spring-boot-starter后,会自动创建一个DataSourceTransactionManager对象,作为事务管理器,因此不需要自己手动的创建。

事务隔离级别

数据库事务具有ACID四个特性,如下:

  • Atomic(原子性)
  • Consistency(一致性)
  • Isolation(隔离性)
  • Durability(持久性)

其中,隔离性这里需要在数据库事务中进行配置的项。在并发环境中,存在数据库中同一条数据被不同的事务进行访问,这样就可能产生丢失更新的问题。因此隔离性的设置就是为了在不同不同程度上解决多个数据库事务同时访问一个数据库数据而产生丢失更新问题。

数据库在高并发环境中会出现几种问题,分别是:

  • 脏读:一个事务读到另一个事务的未提交数据。
  • 不可重复读:事务在前后读取同一条数据时,数据不一致。
  • 幻读:共享的数据对一个事务来说,前后表现出的状态不一致。

为了遏制这几种数据库事务并发问题,设计了如下四种数据库事务隔离级别,并且每种级别可以遏制的并发问题(X号所示)如下表所示:

隔离级别 脏读 不可重复读 幻读
读未提交 ✔️ ✔️ ✔️
读提交(读写提交) X ✔️ ✔️
可重复读 X X ✔️
串行化 X X X

在上述的隔离级别中,虽然对并发环境下数据一致性的保证依次增强,但其对性能的影响也依次升高。因此在选择数据库事务隔离记别时,需要综合考虑使用场景以及数据一致性要求,和性能等问题进行选择。一般而言,隔离级别的选择为读提交为主。

而不同数据库对于数据库的隔离级别的支持也不尽相同,Oracle只能支持读提交和串行化,MySQL可以支持全部四种。Oracle默认为读写提交,MySQL默认为可重复读

在Spring Boot中使用@Transactional注解时,可以对事务隔离级别进行设置,例如:

@Transactional(isolation = Isolation.READ_COMMITTED)

除此之外可以通过Spring Boot的application.properties配置文件设置默认的隔离级别:

# 设置默认的隔离级别为读写提交
# -1 使用数据库默认隔离级别
# 1 读未提交
# 2 读提交
# 3 可重复读
# 4 串行化
spring.datasource.tomcat.default-transaction-isolation=2

事务传播行为

事务的传播行为定义了当一个事务调用另一个事务采取的策略。在Spring 中,当一个方法调用另一个方法时,可以让事务采取不同的策略进行工作,如新建事务执行,挂起当前事务执行等,这就是数据库事务的传播行为。

在对@Transactional注解进行设置事务传播行为时,可以选择的项目可以从如下枚举类型中查看:

public enum Propagation {// 需要事务,为默认传播行为,如果当前方法存在事务,就沿用当前的事务,否则新建一个事务运行子方法REQUIRED(0),// 支持事务,如果当前存在事务沿用当前事务,如果不存在则继续采用无事务的方式运行子方法SUPPORTS(1),// 必须使用事务,如果当前没有事务会抛出异常,如果存在当前事务就沿用当前事务MANDATORY(2),// 无论当前事务是否存在,都会创建新的事务运行子方法,这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立REQUIRES_NEW(3),// 不支持事务,当前存在事务时将当前事务挂起,运行方法NOT_SUPPORTED(4),// 不支持事务,如果当前方法存在事务则抛出异常,否则继续使用无事务机制运行NEVER(5),// 在当前方法调用子方法是,如果子方法发生异常,只回滚子方法的事务,而不回滚当前方法的事务NESTED(6);private final int value;private Propagation(int value) {this.value = value;}public int value() {return this.value;}
}

数据库事务使用实例

下面使用@Transactional注解对数据库事务的简单使用进行说明,这里使用的为Spring Boot+MyBatis来操作MySQL数据库。该项目的前序工作见:Spring Boot整合MyBatis框架操作MySQL数据库实例。这里在此基础上进行数据库事务的使用说明。下面构建相应的方法。

事务的使用

创建用户表

create table t_user (id int(12) not null auto_increment,user_name varchar(60) not null,sex int(3) not null default 1 check (sex in(1,2)),note varchar(256) null,primary key(id)
);

User对象类

@Data
@Alias(value = "user")
public class User {private Long id;@NotNull(message = "用户名不能为空")private String userName;@NotNull(message = "备注不能为空")private String note;@NotNull(message = "性别不能为空")private SexEnum sex;
}

MyBatis映射文件及接口

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.zyt.springbootlearning.dao.UserMapper"><select id="getUserById" parameterType="long" resultType="user">select id, user_name as userName, sex, note from t_user where id = #{id}</select><insert id="insertUser" parameterType="cn.zyt.springbootlearning.domain.User" useGeneratedKeys="true" keyProperty="id">insert into t_user (user_name, sex, note) values (#{userName}, #{sex}, #{note})</insert>
</mapper>

映射接口为:

@Repository
public interface UserMapper {User getUserById(Long id);int insertUser(User user);
}

Service接口和实现类

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate 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 insertUser(User user) {return userMapper.insertUser(user);}
}

在Service中,使用了Transactional注解来标识两个方法开启事务,并并且配置了相应的事务隔离级别为读提交,超时时间为1s。下面使用一个controller来测试一下数据库事务。

Controller方法

@Controller
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;/*** 测试Transactional*/@RequestMapping("/getUser")@ResponseBodypublic User getUser(Long id) {return userService.getUserById(id);}/*** 测试Transactional*/@RequestMapping("/insertUser")@ResponseBodypublic Map<String, Object> insertUser(String userName, int sex, String note) {User user = new User(userName, sex, note);int update = userService.insertUser(user);Map<String, Object> result = new HashMap<>();result.put("success", update == 1);result.put("user", user);return result;}
}

此外为了查看一下在使用MyBatis时,使用的事务管理器,在configuration配置类中加入了如下代码,在这个类对象被初始化后调用后初始化方法来打印出当前的事务管理器的名称:

@Configuration
public class MyBatisConfiguration {@AutowiredPlatformTransactionManager transactionManager;@PostConstructpublic void viewTransactionManager() {System.out.println("-----------------------------------");System.out.println(transactionManager.getClass().getName());System.out.println("-----------------------------------");}
}

上述的内容完成后,启动Spring Boot项目,来进行测试。启动完成后,可以看懂console中有如下的输出,即为当前在引入MyBatis之后使用的事务管理器:

-----------------------------------
org.springframework.jdbc.datasource.DataSourceTransactionManager
-----------------------------------

使用如下URL对开启事务的getUser方法进行测试:

http://localhost:8080/user/getUser?id=1

在Console中查看DEBUG级别的日志输出中包括如下内容:

Creating new transaction with name [cn.zyt.springbootlearning.service.impl.UserServiceImpl.getUserById]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,timeout_1
2020-02-19 17:20:19.186 DEBUG 80529 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [HikariProxyConnection@853423018 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] for JDBC transaction
2020-02-19 17:20:19.187 DEBUG 80529 --- [nio-8080-exec-1] o.s.jdbc.datasource.DataSourceUtils      : Changing isolation level of JDBC Connection [HikariProxyConnection@853423018 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to 2
2020-02-19 17:20:19.229 DEBUG 80529 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [HikariProxyConnection@853423018 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to manual commit
2020-02-19 17:20:19.255 DEBUG 80529 --- [nio-8080-exec-1] org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
2020-02-19 17:20:19.257 DEBUG 80529 --- [nio-8080-exec-1] org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fa1dcd7]
2020-02-19 17:20:19.262 DEBUG 80529 --- [nio-8080-exec-1] o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@853423018 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] will be managed by Spring
2020-02-19 17:20:19.263 DEBUG 80529 --- [nio-8080-exec-1] c.z.s.dao.UserMapper.getUserById         : ==>  Preparing: select id, user_name as userName, sex, note from t_user where id = ?
2020-02-19 17:20:19.278 DEBUG 80529 --- [nio-8080-exec-1] c.z.s.dao.UserMapper.getUserById         : ==> Parameters: 1(Long)
2020-02-19 17:20:19.310 DEBUG 80529 --- [nio-8080-exec-1] c.z.s.dao.UserMapper.getUserById         : <==      Total: 1
2020-02-19 17:20:19.310 DEBUG 80529 --- [nio-8080-exec-1] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fa1dcd7]
2020-02-19 17:20:19.310 DEBUG 80529 --- [nio-8080-exec-1] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fa1dcd7]
2020-02-19 17:20:19.310 DEBUG 80529 --- [nio-8080-exec-1] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fa1dcd7]
2020-02-19 17:20:19.310 DEBUG 80529 --- [nio-8080-exec-1] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fa1dcd7]
2020-02-19 17:20:19.310 DEBUG 80529 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2020-02-19 17:20:19.310 DEBUG 80529 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [HikariProxyConnection@853423018 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be]
2020-02-19 17:20:19.352 DEBUG 80529 --- [nio-8080-exec-1] o.s.jdbc.datasource.DataSourceUtils      : Resetting isolation level of JDBC Connection [HikariProxyConnection@853423018 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to 4
2020-02-19 17:20:19.372 DEBUG 80529 --- [nio-8080-exec-1] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [HikariProxyConnection@853423018 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] after transaction

同样的在使用如下URL进行insertUser时一样开启了事务:

http://localhost:8080/user/insertUser?userName=tname&sex=1&note=none

日志输出如下:

2020-02-19 17:22:57.875 DEBUG 80529 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [cn.zyt.springbootlearning.service.impl.UserServiceImpl.insertUser]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED,timeout_1
2020-02-19 17:22:57.896 DEBUG 80529 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [HikariProxyConnection@678085356 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] for JDBC transaction
2020-02-19 17:22:57.896 DEBUG 80529 --- [nio-8080-exec-5] o.s.jdbc.datasource.DataSourceUtils      : Changing isolation level of JDBC Connection [HikariProxyConnection@678085356 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to 2
2020-02-19 17:22:57.938 DEBUG 80529 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [HikariProxyConnection@678085356 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to manual commit
2020-02-19 17:22:57.960 DEBUG 80529 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
2020-02-19 17:22:57.960 DEBUG 80529 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@53d67f36]
2020-02-19 17:22:57.960 DEBUG 80529 --- [nio-8080-exec-5] o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@678085356 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] will be managed by Spring
2020-02-19 17:22:57.960 DEBUG 80529 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : ==>  Preparing: insert into t_user (user_name, sex, note) values (?, ?, ?)
2020-02-19 17:22:57.962 DEBUG 80529 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : ==> Parameters: tname(String), 1(Integer), none(String)
2020-02-19 17:22:57.983 DEBUG 80529 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : <==    Updates: 1
2020-02-19 17:22:57.984 DEBUG 80529 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@53d67f36]
2020-02-19 17:22:57.984 DEBUG 80529 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@53d67f36]
2020-02-19 17:22:57.985 DEBUG 80529 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@53d67f36]
2020-02-19 17:22:57.985 DEBUG 80529 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@53d67f36]
2020-02-19 17:22:57.985 DEBUG 80529 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2020-02-19 17:22:57.985 DEBUG 80529 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [HikariProxyConnection@678085356 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be]
2020-02-19 17:22:58.026 DEBUG 80529 --- [nio-8080-exec-5] o.s.jdbc.datasource.DataSourceUtils      : Resetting isolation level of JDBC Connection [HikariProxyConnection@678085356 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to 4
2020-02-19 17:22:58.047 DEBUG 80529 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [HikariProxyConnection@678085356 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] after transaction

设置事务传播行为

下面通过一个批量创建用户的过程来测试事务的传播行为。首先创建用于批量创建用户的Service接口和实现类:

@Service
public class UserBatchSeviceImpl implements UserBatchService {@Autowiredprivate UserService userService;/*** 数据库事务传播机制REQUESTED*/@Override@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)public int insertUsers(List<User> userList) {int count = 0;for (User user : userList) {// 调用子方法,将使用@Transactional中定义的传播行为count += userService.insertUser(user);}return count;}
}

注意上述代码,其中将该insertUsers方法的事务传播行为设置为REQUIRED,而insertUser方法没有设置传播行为,因此该子方法insertUser将沿用insertUses的事务(即同一个事务)。下面创建一个一个Controller来进行测试:

    /*** 测试Transactional数据库事务隔离机制和传播方式*/@RequestMapping("/insertUsers")@ResponseBodypublic Map<String, Object> insertBatchUser(String userName1, int sex1, String note1,String userName2, int sex2, String note2) {User user1 = new User(userName1, sex1, note1);User user2 = new User(userName2, sex2, note2);List<User> userList = new ArrayList<>();userList.add(user1);userList.add(user2);int insertCount = userBatchService.insertUsers(userList);Map<String, Object> result = new HashMap<>();result.put("success", insertCount > 0);result.put("user", userList);return result;}

使用如下的URL进行测试:

http://localhost:8080/user/insertUsers?userName1=tname1&sex1=1&note1=none&userName2=tname2&sex2=1&note2=none

日志输出如下:

# 为insertUsers方法创建new transaction
2020-02-19 17:36:11.803 DEBUG 80529 --- [nio-8080-exec-2] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [cn.zyt.springbootlearning.service.impl.UserBatchSeviceImpl.insertUsers]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED
2020-02-19 17:36:11.823 DEBUG 80529 --- [nio-8080-exec-2] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [HikariProxyConnection@207102938 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] for JDBC transaction
2020-02-19 17:36:11.824 DEBUG 80529 --- [nio-8080-exec-2] o.s.jdbc.datasource.DataSourceUtils      : Changing isolation level of JDBC Connection [HikariProxyConnection@207102938 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to 2
2020-02-19 17:36:11.865 DEBUG 80529 --- [nio-8080-exec-2] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [HikariProxyConnection@207102938 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to manual commit
# 使用已经存在的事务
2020-02-19 17:36:11.888 DEBUG 80529 --- [nio-8080-exec-2] o.s.j.d.DataSourceTransactionManager     : Participating in existing transaction
2020-02-19 17:36:11.888 DEBUG 80529 --- [nio-8080-exec-2] org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
2020-02-19 17:36:11.888 DEBUG 80529 --- [nio-8080-exec-2] org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@33f6439d]
2020-02-19 17:36:11.888 DEBUG 80529 --- [nio-8080-exec-2] o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@207102938 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] will be managed by Spring
2020-02-19 17:36:11.888 DEBUG 80529 --- [nio-8080-exec-2] c.z.s.dao.UserMapper.insertUser          : ==>  Preparing: insert into t_user (user_name, sex, note) values (?, ?, ?)
2020-02-19 17:36:11.888 DEBUG 80529 --- [nio-8080-exec-2] c.z.s.dao.UserMapper.insertUser          : ==> Parameters: tname1(String), 1(Integer), none(String)
2020-02-19 17:36:11.909 DEBUG 80529 --- [nio-8080-exec-2] c.z.s.dao.UserMapper.insertUser          : <==    Updates: 1
2020-02-19 17:36:11.909 DEBUG 80529 --- [nio-8080-exec-2] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@33f6439d]
# 使用已经存在的事务
2020-02-19 17:36:11.909 DEBUG 80529 --- [nio-8080-exec-2] o.s.j.d.DataSourceTransactionManager     : Participating in existing transaction
2020-02-19 17:36:11.909 DEBUG 80529 --- [nio-8080-exec-2] org.mybatis.spring.SqlSessionUtils       : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@33f6439d] from current transaction
2020-02-19 17:36:11.909 DEBUG 80529 --- [nio-8080-exec-2] c.z.s.dao.UserMapper.insertUser          : ==>  Preparing: insert into t_user (user_name, sex, note) values (?, ?, ?)
2020-02-19 17:36:11.910 DEBUG 80529 --- [nio-8080-exec-2] c.z.s.dao.UserMapper.insertUser          : ==> Parameters: tname2(String), 1(Integer), none(String)
2020-02-19 17:36:11.930 DEBUG 80529 --- [nio-8080-exec-2] c.z.s.dao.UserMapper.insertUser          : <==    Updates: 1
2020-02-19 17:36:11.931 DEBUG 80529 --- [nio-8080-exec-2] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@33f6439d]
2020-02-19 17:36:11.931 DEBUG 80529 --- [nio-8080-exec-2] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@33f6439d]
2020-02-19 17:36:11.931 DEBUG 80529 --- [nio-8080-exec-2] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@33f6439d]
2020-02-19 17:36:11.931 DEBUG 80529 --- [nio-8080-exec-2] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@33f6439d]
2020-02-19 17:36:11.931 DEBUG 80529 --- [nio-8080-exec-2] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2020-02-19 17:36:11.931 DEBUG 80529 --- [nio-8080-exec-2] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [HikariProxyConnection@207102938 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be]
2020-02-19 17:36:11.972 DEBUG 80529 --- [nio-8080-exec-2] o.s.jdbc.datasource.DataSourceUtils      : Resetting isolation level of JDBC Connection [HikariProxyConnection@207102938 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] to 4
2020-02-19 17:36:11.994 DEBUG 80529 --- [nio-8080-exec-2] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [HikariProxyConnection@207102938 wrapping com.mysql.jdbc.JDBC4Connection@568ed1be] after transaction

此时事务的执行示意图如下:

下面为子方法insertUser的设置相应的数据库传播行为如下:

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

重启项目再次测试insertUsers,查看日志如下:


# 创建当前方法的事务(insertUsers)
2020-02-19 21:30:43.061 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [cn.zyt.springbootlearning.service.impl.UserBatchSeviceImpl.insertUsers]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED
2020-02-19 21:30:43.080 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [HikariProxyConnection@1925691138 wrapping com.mysql.jdbc.JDBC4Connection@51cdb74] for JDBC transaction
2020-02-19 21:30:43.082 DEBUG 81249 --- [nio-8080-exec-6] o.s.jdbc.datasource.DataSourceUtils      : Changing isolation level of JDBC Connection [HikariProxyConnection@1925691138 wrapping com.mysql.jdbc.JDBC4Connection@51cdb74] to 2
2020-02-19 21:30:43.122 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [HikariProxyConnection@1925691138 wrapping com.mysql.jdbc.JDBC4Connection@51cdb74] to manual commit
# 创建子方法insertUser的新事务
2020-02-19 21:30:43.147 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Suspending current transaction, creating new transaction with name [cn.zyt.springbootlearning.service.impl.UserServiceImpl.insertUser]
2020-02-19 21:30:43.171 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [HikariProxyConnection@853398429 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] for JDBC transaction
2020-02-19 21:30:43.171 DEBUG 81249 --- [nio-8080-exec-6] o.s.jdbc.datasource.DataSourceUtils      : Changing isolation level of JDBC Connection [HikariProxyConnection@853398429 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] to 2
2020-02-19 21:30:43.216 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [HikariProxyConnection@853398429 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] to manual commit
2020-02-19 21:30:43.246 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
2020-02-19 21:30:43.248 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@42b127f0]
2020-02-19 21:30:43.253 DEBUG 81249 --- [nio-8080-exec-6] o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@853398429 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] will be managed by Spring
2020-02-19 21:30:43.254 DEBUG 81249 --- [nio-8080-exec-6] c.z.s.dao.UserMapper.insertUser          : ==>  Preparing: insert into t_user (user_name, sex, note) values (?, ?, ?)
2020-02-19 21:30:43.270 DEBUG 81249 --- [nio-8080-exec-6] c.z.s.dao.UserMapper.insertUser          : ==> Parameters: tname1(String), 1(Integer), none(String)
2020-02-19 21:30:43.295 DEBUG 81249 --- [nio-8080-exec-6] c.z.s.dao.UserMapper.insertUser          : <==    Updates: 1
2020-02-19 21:30:43.296 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@42b127f0]
2020-02-19 21:30:43.296 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@42b127f0]
2020-02-19 21:30:43.296 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@42b127f0]
2020-02-19 21:30:43.296 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@42b127f0]
2020-02-19 21:30:43.296 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2020-02-19 21:30:43.296 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [HikariProxyConnection@853398429 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77]
2020-02-19 21:30:43.341 DEBUG 81249 --- [nio-8080-exec-6] o.s.jdbc.datasource.DataSourceUtils      : Resetting isolation level of JDBC Connection [HikariProxyConnection@853398429 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] to 4
2020-02-19 21:30:43.364 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [HikariProxyConnection@853398429 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] after transaction
2020-02-19 21:30:43.364 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Resuming suspended transaction after completion of inner transaction
# 创建子方法insertUser的新事务
2020-02-19 21:30:43.365 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Suspending current transaction, creating new transaction with name [cn.zyt.springbootlearning.service.impl.UserServiceImpl.insertUser]
2020-02-19 21:30:43.365 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [HikariProxyConnection@1482450123 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] for JDBC transaction
2020-02-19 21:30:43.365 DEBUG 81249 --- [nio-8080-exec-6] o.s.jdbc.datasource.DataSourceUtils      : Changing isolation level of JDBC Connection [HikariProxyConnection@1482450123 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] to 2
2020-02-19 21:30:43.410 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [HikariProxyConnection@1482450123 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] to manual commit
2020-02-19 21:30:43.433 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
2020-02-19 21:30:43.433 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6bab40fc]
2020-02-19 21:30:43.433 DEBUG 81249 --- [nio-8080-exec-6] o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@1482450123 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] will be managed by Spring
2020-02-19 21:30:43.433 DEBUG 81249 --- [nio-8080-exec-6] c.z.s.dao.UserMapper.insertUser          : ==>  Preparing: insert into t_user (user_name, sex, note) values (?, ?, ?)
2020-02-19 21:30:43.433 DEBUG 81249 --- [nio-8080-exec-6] c.z.s.dao.UserMapper.insertUser          : ==> Parameters: tname2(String), 1(Integer), none(String)
2020-02-19 21:30:43.456 DEBUG 81249 --- [nio-8080-exec-6] c.z.s.dao.UserMapper.insertUser          : <==    Updates: 1
2020-02-19 21:30:43.456 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6bab40fc]
2020-02-19 21:30:43.456 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6bab40fc]
2020-02-19 21:30:43.456 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6bab40fc]
2020-02-19 21:30:43.456 DEBUG 81249 --- [nio-8080-exec-6] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6bab40fc]
2020-02-19 21:30:43.456 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2020-02-19 21:30:43.456 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [HikariProxyConnection@1482450123 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77]
2020-02-19 21:30:43.501 DEBUG 81249 --- [nio-8080-exec-6] o.s.jdbc.datasource.DataSourceUtils      : Resetting isolation level of JDBC Connection [HikariProxyConnection@1482450123 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] to 4
2020-02-19 21:30:43.523 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [HikariProxyConnection@1482450123 wrapping com.mysql.jdbc.JDBC4Connection@4b3ad77] after transaction
2020-02-19 21:30:43.523 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Resuming suspended transaction after completion of inner transaction
2020-02-19 21:30:43.524 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2020-02-19 21:30:43.524 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [HikariProxyConnection@1925691138 wrapping com.mysql.jdbc.JDBC4Connection@51cdb74]
2020-02-19 21:30:43.562 DEBUG 81249 --- [nio-8080-exec-6] o.s.jdbc.datasource.DataSourceUtils      : Resetting isolation level of JDBC Connection [HikariProxyConnection@1925691138 wrapping com.mysql.jdbc.JDBC4Connection@51cdb74] to 4
2020-02-19 21:30:43.580 DEBUG 81249 --- [nio-8080-exec-6] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [HikariProxyConnection@1925691138 wrapping com.mysql.jdbc.JDBC4Connection@51cdb74] after transaction

此时事务的传播行为示例如下:

下面将insertUser方法的事务传播级别设置为NESTED,如下:

    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.NESTED, timeout = 1)public int insertUser(User user) {return userMapper.insertUser(user);}

此时如果子方法中出现异常,会回滚子方法的事务而不会回滚当前事务。日志输出如下:


# 创建当前事务
2020-02-19 21:41:07.711 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Creating new transaction with name [cn.zyt.springbootlearning.service.impl.UserBatchSeviceImpl.insertUsers]: PROPAGATION_REQUIRED,ISOLATION_READ_COMMITTED
2020-02-19 21:41:07.734 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Acquired Connection [HikariProxyConnection@1337356240 wrapping com.mysql.jdbc.JDBC4Connection@480647a1] for JDBC transaction
2020-02-19 21:41:07.734 DEBUG 81319 --- [nio-8080-exec-5] o.s.jdbc.datasource.DataSourceUtils      : Changing isolation level of JDBC Connection [HikariProxyConnection@1337356240 wrapping com.mysql.jdbc.JDBC4Connection@480647a1] to 2
2020-02-19 21:41:07.779 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Switching JDBC Connection [HikariProxyConnection@1337356240 wrapping com.mysql.jdbc.JDBC4Connection@480647a1] to manual commit
# 创建子方法事务
2020-02-19 21:41:07.807 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Creating nested transaction with name [cn.zyt.springbootlearning.service.impl.UserServiceImpl.insertUser]
2020-02-19 21:41:07.831 DEBUG 81319 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Creating a new SqlSession
2020-02-19 21:41:07.831 DEBUG 81319 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@458d82e]
2020-02-19 21:41:07.832 DEBUG 81319 --- [nio-8080-exec-5] o.m.s.t.SpringManagedTransaction         : JDBC Connection [HikariProxyConnection@1337356240 wrapping com.mysql.jdbc.JDBC4Connection@480647a1] will be managed by Spring
2020-02-19 21:41:07.832 DEBUG 81319 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : ==>  Preparing: insert into t_user (user_name, sex, note) values (?, ?, ?)
2020-02-19 21:41:07.832 DEBUG 81319 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : ==> Parameters: tname1(String), 1(Integer), none(String)
2020-02-19 21:41:07.855 DEBUG 81319 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : <==    Updates: 1
2020-02-19 21:41:07.856 DEBUG 81319 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@458d82e]
# 释放savapoint
2020-02-19 21:41:07.856 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Releasing transaction savepoint
2020-02-19 21:41:07.856 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Creating nested transaction with name [cn.zyt.springbootlearning.service.impl.UserServiceImpl.insertUser]
2020-02-19 21:41:07.879 DEBUG 81319 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Fetched SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@458d82e] from current transaction
2020-02-19 21:41:07.879 DEBUG 81319 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : ==>  Preparing: insert into t_user (user_name, sex, note) values (?, ?, ?)
2020-02-19 21:41:07.879 DEBUG 81319 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : ==> Parameters: tname2(String), 1(Integer), none(String)
2020-02-19 21:41:07.902 DEBUG 81319 --- [nio-8080-exec-5] c.z.s.dao.UserMapper.insertUser          : <==    Updates: 1
2020-02-19 21:41:07.902 DEBUG 81319 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@458d82e]
# 释放savapoint
2020-02-19 21:41:07.903 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Releasing transaction savepoint
2020-02-19 21:41:07.903 DEBUG 81319 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@458d82e]
2020-02-19 21:41:07.903 DEBUG 81319 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@458d82e]
2020-02-19 21:41:07.903 DEBUG 81319 --- [nio-8080-exec-5] org.mybatis.spring.SqlSessionUtils       : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@458d82e]
2020-02-19 21:41:07.903 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Initiating transaction commit
2020-02-19 21:41:07.903 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Committing JDBC transaction on Connection [HikariProxyConnection@1337356240 wrapping com.mysql.jdbc.JDBC4Connection@480647a1]
2020-02-19 21:41:07.947 DEBUG 81319 --- [nio-8080-exec-5] o.s.jdbc.datasource.DataSourceUtils      : Resetting isolation level of JDBC Connection [HikariProxyConnection@1337356240 wrapping com.mysql.jdbc.JDBC4Connection@480647a1] to 4
2020-02-19 21:41:07.970 DEBUG 81319 --- [nio-8080-exec-5] o.s.j.d.DataSourceTransactionManager     : Releasing JDBC Connection [HikariProxyConnection@1337356240 wrapping com.mysql.jdbc.JDBC4Connection@480647a1] after transaction

可以看到NESTED传播行为,之所以可以实现子事务回滚而当前事务不回滚,使用了savepoint的机制。

此外,NESTED传播行为和REQUIRES_NEW还是有区别的,NESTED传播行为会沿用当前事务的隔离级别和锁等特性,而REQURIES_NEW则可以拥有子事务独立的隔离级别和锁等特性。

Spring Boot中声明式数据库事务使用与理解相关推荐

  1. Spring Boot中使用PostgreSQL数据库

    在如今的关系型数据库中,有两个开源产品是你必须知道的.其中一个是MySQL,相信关注我的小伙伴们一定都不陌生,因为之前的Spring Boot关于关系型数据库的所有例子都是对MySQL来介绍的.而今天 ...

  2. 8.Spring整合Hibernate_2_声明式的事务管理(Annotation的方式)

    声明式的事务管理(AOP的主要用途之一) (Annotation的方式) 1.加入annotation.xsd 2.加入txManager bean 3.<tx:annotation-drive ...

  3. Spring Boot中使用MongoDB数据库

    MongoDB简介 MongoDB是一个基于分布式文件存储的数据库,它是一个介于关系数据库和非关系数据库之间的产品,其主要目标是在键/值存储方式(提供了高性能和高度伸缩性)和传统的RDBMS系统(具有 ...

  4. Spring Boot中使用时序数据库InfluxDB

    除了最常用的关系数据库和缓存之外,之前我们已经介绍了在Spring Boot中如何配置和使用MongoDB.LDAP这些存储的案例.接下来,我们继续介绍另一种特殊的数据库:时序数据库InfluxDB在 ...

  5. Spring Boot中使用Redis数据库

    Spring Boot中除了对常用的关系型数据库提供了优秀的自动化支持之外,对于很多NoSQL数据库一样提供了自动化配置的支持,包括:Redis, MongoDB, Elasticsearch, So ...

  6. Spring Boot中DAO层和Service层等理解

    Spring Boot中DAO等层的理解 1 DAO层 2 Service层 3 Controller层 4 Entity层 5 View层 6 Utils层 总结 目前在公司实习python算法研究 ...

  7. Spring Boot中使用多数据库

    开发企业应用时我们常常遇到要同时访问多种不同数据库的问题,有时是必须把数据归档到某种数据仓库中,有时是要把数据变更推送到第三方数据库中.使用Spring框架时,使用单一数据库是非常容易的,但如果要同时 ...

  8. Spring Boot学习总结(27)—— Spring Boot中两个数据库迁移工具Liquibase和Flyway的比较

    前言 当您需要使用Java创建Web应用程序或API时,可以使用RESTful,SOAP或GraphQL.无论您是查看同步HTTP,异步还是反应式,队列中的消息或来自Kafka的事件,都很难超越Spr ...

  9. spring mysql mongdb_Spring Boot中使用MongoDB数据库的方法

    MongoDB数据库简介 简介 MongoDB是一个高性能,开源,无模式的,基于分布式文件存储的文档型数据库,由C++语言编写,其名称来源取自"humongous",是一种开源的文 ...

最新文章

  1. 转载 load-on-startup的用法
  2. Leangoo领歌敏捷工具新增测试管理功能
  3. 最大元最小元上确界_托盘天平最大秤量和最小秤量
  4. 使用Java合并图片、修改DPI
  5. 网站导航栏如何设置更利于提升SEO优化效果?
  6. WebServices中使用cxf开发日志拦截器以及自定义拦截器
  7. 七种武器——.NET工程师求职面试必杀技
  8. mysql myisam 支持事务吗_第三章(附)mysql表类型MyISAM和InnoDB区别(决定了是否支持事务)...
  9. java ee面试题专家总结(必看),Java EE面试题专家总结(必看)
  10. SQL在SQL Server中相交使用
  11. 11-----的使用
  12. HTTP无状态协议到底指的什么?
  13. 迪文屏幕的学习和开发
  14. VMWare虚拟机启动img文件
  15. 在线qq的html代码,网页QQ
  16. 给自己分一个 MAC地址--locally administered address
  17. mysql日期转为周数
  18. ​PDF文件怎么转换成JPG图片?分享两种简单的转换方法
  19. 官方指标监控神器SpringBootAdmin保姆级教程
  20. [英语语法]句法之定语从句

热门文章

  1. python创建文件夹
  2. iOS 16升级引热议,网友吐槽锁屏太花哨,潘粤明更新后一度登不上微信
  3. ajax删除节点,jQuery实现删除li节点的方法
  4. html设置表格列宽百 分比,CSS属性中经常出现的百分比
  5. 小艺人黄鑫洋受邀参加巴黎时装周儿童单元武汉站
  6. matlab画BODE图GUI设计
  7. 2020级大学生计算机,2021年大学生笔记本电脑推荐
  8. 简述面向对象编程原则和设计模式
  9. Maven 的assembly插件使用
  10. Arduino esp8266气象时钟当中对象指针的运用