29.事务处理

 

SpringBoot中推荐使用@Transactional注解来声明事务。只需要在需要事务控制的方法或类(全部方法有效)上增加@Transactional注解。原理是SpringBoot会自动默认分别注入DataSourceTransactionManagerJpaTransactionManager

项目工程目录结构:

SpringBoot实现事务处理:

1.pom.xml引入aop的依赖,因为事务是基于aop实现的,在方法执行前开启事务,在方法执行后提交事务或者回滚事务

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.启动类添加@EnableTransactionManagement注解,开启事务

3.在Service层添加@Transactional注解,在类或者方法上添加@Transactional注解,添加到类上为该类的所有public方法都开启事务,添加到方法上为该方法开启事务。同时写了类级别和方法级别的@Transactional注解,方法级别的@Transactional注解的优先级更高。

(1) 实体类: MyDirectory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.example.demo.entity;

public class MyDirectory {
private Integer directoryId;

private String directoryName;

private String remark;

private Integer userId;

public MyDirectory() {
super();
}

public MyDirectory(String directoryName, String remark, Integer userId) {
super();
this.directoryName = directoryName;
this.remark = remark;
this.userId = userId;
}

public MyDirectory(Integer directoryId, String directoryName, String remark, Integer userId) {
super();
this.directoryId = directoryId;
this.directoryName = directoryName;
this.remark = remark;
this.userId = userId;
}

public Integer getDirectoryId() {
return directoryId;
}

public void setDirectoryId(Integer directoryId) {
this.directoryId = directoryId;
}

public String getDirectoryName() {
return directoryName;
}

public void setDirectoryName(String directoryName) {
this.directoryName = directoryName == null ? null : directoryName.trim();
}

public String getRemark() {
return remark;
}

public void setRemark(String remark) {
this.remark = remark == null ? null : remark.trim();
}

public Integer getUserId() {
return userId;
}

public void setUserId(Integer userId) {
this.userId = userId;
}

@Override
public String toString() {
return "MyDirectory [directoryId=" + directoryId + ", directoryName=" + directoryName + ", remark=" + remark
+ ", userId=" + userId + "]";
}

}

(2) Dao

接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.demo.dao;

import java.util.List;

import com.example.demo.entity.MyDirectory;

public interface MyDirectoryMapper {

int deleteByPrimaryKey(Integer directoryId);

int insert(MyDirectory record);

MyDirectory selectByPrimaryKey(Integer directoryId);

int updateByPrimaryKey(MyDirectory record);

public List<MyDirectory> getAllDirectory();

}

Mapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
<?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.example.demo.dao.MyDirectoryMapper" >
<resultMap id="BaseResultMap" type="com.example.demo.entity.MyDirectory" >
<id column="directory_id" property="directoryId" jdbcType="INTEGER" />
<result column="directory_name" property="directoryName" jdbcType="VARCHAR" />
<result column="remark" property="remark" jdbcType="VARCHAR" />
<result column="user_id" property="userId" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List" >
directory_id, directory_name, remark, user_id
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
select
<include refid="Base_Column_List" />
from t_directory
where directory_id = #{directoryId,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer" >
delete from t_directory
where directory_id = #{directoryId,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.example.demo.entity.MyDirectory" >
insert into t_directory (directory_id, directory_name, remark,
user_id)
values (#{directoryId,jdbcType=INTEGER}, #{directoryName,jdbcType=VARCHAR}, #{remark,jdbcType=VARCHAR},
#{userId,jdbcType=INTEGER})
</insert>
<insert id="insertSelective" parameterType="com.example.demo.entity.MyDirectory" >
insert into t_directory
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="directoryId != null" >
directory_id,
</if>
<if test="directoryName != null" >
directory_name,
</if>
<if test="remark != null" >
remark,
</if>
<if test="userId != null" >
user_id,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="directoryId != null" >
#{directoryId,jdbcType=INTEGER},
</if>
<if test="directoryName != null" >
#{directoryName,jdbcType=VARCHAR},
</if>
<if test="remark != null" >
#{remark,jdbcType=VARCHAR},
</if>
<if test="userId != null" >
#{userId,jdbcType=INTEGER},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.example.demo.entity.MyDirectory" >
update t_directory
<set >
<if test="directoryName != null" >
directory_name = #{directoryName,jdbcType=VARCHAR},
</if>
<if test="remark != null" >
remark = #{remark,jdbcType=VARCHAR},
</if>
<if test="userId != null" >
user_id = #{userId,jdbcType=INTEGER},
</if>
</set>
where directory_id = #{directoryId,jdbcType=INTEGER}
</update>
<update id="updateByPrimaryKey" parameterType="com.example.demo.entity.MyDirectory" >
update t_directory
set directory_name = #{directoryName,jdbcType=VARCHAR},
remark = #{remark,jdbcType=VARCHAR},
user_id = #{userId,jdbcType=INTEGER}
where directory_id = #{directoryId,jdbcType=INTEGER}
</update>

<!-- 查询所有文件夹 -->
<select id="getAllDirectory" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from t_directory
</select>

</mapper>

(3) Service

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.demo.service;

import java.util.List;

import com.example.demo.entity.MyDirectory;

public interface MyDirectoryService {

int deleteByPrimaryKey(Integer directoryId);

int insert(MyDirectory record);

MyDirectory selectByPrimaryKey(Integer directoryId);

int updateByPrimaryKey(MyDirectory record);

public List<MyDirectory> getAllDirectory();

}

实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.example.demo.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.dao.MyDirectoryMapper;
import com.example.demo.entity.MyDirectory;
import com.example.demo.service.MyDirectoryService;

@Service
@Transactional
public class MyDirectoryServiceImpl implements MyDirectoryService {

@Autowired
private MyDirectoryMapper myDirectoryMapper;

@Override
public int deleteByPrimaryKey(Integer directoryId) {
return myDirectoryMapper.deleteByPrimaryKey(directoryId);
}

@Override
public int insert(MyDirectory record) {
return myDirectoryMapper.insert(record);
}

@Override
@Transactional(readOnly = true)
public MyDirectory selectByPrimaryKey(Integer directoryId) {
return myDirectoryMapper.selectByPrimaryKey(directoryId);
}

@Override
public int updateByPrimaryKey(MyDirectory record) {
return myDirectoryMapper.updateByPrimaryKey(record);
}

@Override
@Transactional(readOnly = true)
public List<MyDirectory> getAllDirectory() {
return myDirectoryMapper.getAllDirectory();
}

}

(4) 启动类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
@MapperScan(basePackages = {"com.example.demo.dao"})
// 开启事务
@EnableTransactionManagement
public class Application {

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

}

(5) 配置文件

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 数据源配置信息
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/demo_filesystem
username: root
password: 123456
initial-size: 3
min-idle: 2
max-active: 10
max-wait: 15000

stat-view-servlet:
login-username: admin
login-password: 123456
# allow和deny可以为空
allow: 192.168.10.25,127.0.0.1,192.168.30.23,192.168.5.26
deny:
url-pattern: /druid/*
# 默认为false,不开启
enabled: true

# mybatis
mybatis:
mapper-locations:
- classpath:mapper/*Mapper.xml
# 配置输出日志
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

log4j.properties

1
2
3
4
5
6
7
8
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# MyBatis logging configuration...
log4j.logger.org.mybatis.example.BlogMapper=TRACE
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n

(6) pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>18_springboot_transaction</artifactId>
<version>1.0</version>
<name>18_springboot_transaction</name>
<description>Spring Boot transaction</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.9</version>
</dependency>

<!-- mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>

<!-- spring-boot-starter-log4j -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>

<!-- pagehelper-spring-boot-starter -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>

<!-- spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

(7) 测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.example.demo;

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

import com.example.demo.entity.MyDirectory;
import com.example.demo.service.MyDirectoryService;

@SpringBootTest
class ApplicationTests {

@Autowired
private MyDirectoryService myDirectoryService;

@Test
public void testPageHelper1() {
MyDirectory myDirectory = new MyDirectory("lib", "dependency jar", 2);
int rows = myDirectoryService.insert(myDirectory);
System.out.println("insert rows = " + rows);


}

}

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2022-06-18 16:55:17.394  INFO 11332 --- [           main] com.example.demo.ApplicationTests        : Starting ApplicationTests using Java 1.8.0_271 on DESKTOP-IOB28AF with PID 11332 (started by Tom in E:\SpringToolSuite\project\demo-4.8.1\18_springboot_transaction)
2022-06-18 16:55:17.395 INFO 11332 --- [ main] com.example.demo.ApplicationTests : No active profile set, falling back to 1 default profile: "default"
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
2022-06-18 16:55:18.040 INFO 11332 --- [ main] c.a.d.s.b.a.DruidDataSourceAutoConfigure : Init DruidDataSource
2022-06-18 16:55:19.084 INFO 11332 --- [ main] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
Parsed mapper file: 'file [E:\SpringToolSuite\project\demo-4.8.1\18_springboot_transaction\target\classes\mapper\MyDirectoryMapper.xml]'


,------. ,--. ,--. ,--.
| .--. ' ,--,--. ,---. ,---. | '--' | ,---. | | ,---. ,---. ,--.--.
| '--' | ' ,-. | | .-. | | .-. : | .--. | | .-. : | | | .-. | | .-. : | .--'
| | --' \ '-' | ' '-' ' \ --. | | | | \ --. | | | '-' ' \ --. | |
`--' `--`--' .`- / `----' `--' `--' `----' `--' | |-' `----' `--'
`---' `--' is intercepting.

2022-06-18 16:55:19.488 INFO 11332 --- [ main] com.example.demo.ApplicationTests : Started ApplicationTests in 2.373 seconds (JVM running for 3.4)
Creating a new SqlSession
Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a4d582c]
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5634a861] will be managed by Spring
==> Preparing: insert into t_directory (directory_id, directory_name, remark, user_id) values (?, ?, ?, ?)
==> Parameters: null, lib(String), dependency jar(String), 2(Integer)
<== Updates: 1
Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a4d582c]
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a4d582c]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a4d582c]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7a4d582c]
insert rows = 1
2022-06-18 16:55:19.827 INFO 11332 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closing ...
2022-06-18 16:55:19.831 INFO 11332 --- [ionShutdownHook] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} closed