# Spring整合MyBatis_声明式事务

# 一、Spring整合MyBatis基础工程搭建 了解

新建Maven工程,导入坐标,pom.xml配置如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qfedu</groupId>
    <artifactId>spring_mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.5</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>
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

基础工程就是之前我们学习MyBatis时使用的工程,里面没有任何Spring的内容。

# 1.1、建库建表

DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `roleName` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `roleDesc` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES (1, '校长', '负责全面工作');
INSERT INTO `sys_role` VALUES (2, '教研专员', '课程研发工作');
INSERT INTO `sys_role` VALUES (3, '讲师', '授课工作');
INSERT INTO `sys_role` VALUES (4, '班主任', '班级日常管理,协助解决学生的问题');
INSERT INTO `sys_role` VALUES (5, '就业专员', '负责学员就业工作');

-- ----------------------------
-- Table structure for sys_user
-- ----------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `email` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(80) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `phoneNum` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_user
-- ----------------------------
INSERT INTO `sys_user` VALUES (1, '张三', 'zhangsan@126.com', '111', '18660701111');
INSERT INTO `sys_user` VALUES (2, '王五', 'wangwu@126.com', '222', '18660702222');
INSERT INTO `sys_user` VALUES (3, '李华', 'lihua@126.com', '333', '18660703333');

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role`  (
  `userId` bigint(20) NOT NULL,
  `roleId` bigint(20) NOT NULL,
  PRIMARY KEY (`userId`, `roleId`) USING BTREE,
  INDEX `roleId`(`roleId`) USING BTREE,
  CONSTRAINT `sys_user_role_ibfk_1` FOREIGN KEY (`userId`) REFERENCES `sys_user` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
  CONSTRAINT `sys_user_role_ibfk_2` FOREIGN KEY (`roleId`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES (1, 1);
INSERT INTO `sys_user_role` VALUES (1, 2);
INSERT INTO `sys_user_role` VALUES (2, 2);
INSERT INTO `sys_user_role` VALUES (2, 3);
INSERT INTO `sys_user_role` VALUES (3, 5);
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

# 1.2、创建实体类

Role.java

public class Role {

    private Long id;
    private String roleName;
    private String roleDesc;
    
    //set和get方法
    //toString方法
}
1
2
3
4
5
6
7
8
9

User.java

public class User {
    private Long id;
    private String username;
    private String email;
    private String password;
    private String phoneNum;
    private List<Role> roles;
    
    //set和get方法
    //toString方法
}
1
2
3
4
5
6
7
8
9
10
11

# 1.3、创建接口

RoleMapper.java

public interface RoleMapper {
    List<Role> list();

    void save(Role role);

    List<Role> fingByUserId(Long uid);
}
1
2
3
4
5
6
7

UserMapper.java

public interface UserMapper {
    List<User> list();

    void save(User user);

    void delete(Long uid);
}
1
2
3
4
5
6
7

UserRoleMapper.java

public interface UserRoleMapper {

    void save(@Param("uid") Long uid, @Param("rid") Long rid);

    void delete(Long uid);
}
1
2
3
4
5
6

# 1.4、创建映射配置文件

RoleMapper.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.qfedu.mapper.RoleMapper">
    <select id="list" resultType="role">
        select * from sys_role
    </select>

    <select id="fingByUserId" resultType="role" parameterType="long">
        select * from sys_user u, sys_user_role ur, sys_role r where u.id=ur.userId and ur.roleId=r.id and u.id=#{id}
    </select>

    <insert id="save" parameterType="role">
        insert into sys_role(roleName, roleDesc) values(#{roleName}, #{roleDesc})
    </insert>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

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.qfedu.mapper.UserMapper">
    <resultMap id="userMap" type="user">
        <id column="id" property="id" />
        <result column="username" property="username" />
        <result column="email" property="email" />
        <result column="password" property="password" />
        <result column="phoneNum" property="phoneNum" />
        <collection property="roles" ofType="role">
            <id column="rid" property="id" />
            <result column="roleName" property="roleName" />
            <result column="roleDesc" property="roleDesc" />
        </collection>
    </resultMap>
    
    <select id="list" resultType="user" resultMap="userMap">
        SELECT
            u.*, r.id rid, r.roleDesc roleDesc, r.roleName roleName
        FROM
            sys_user u, sys_user_role ur, sys_role r
        WHERE
            u.id=ur.userId AND r.id=ur.roleId
    </select>

    <insert id="save" parameterType="user">
        <selectKey keyProperty="id" keyColumn="id" resultType="java.lang.Long" order="AFTER">
            select last_insert_id()
        </selectKey>
        insert into sys_user(username, email, password, phoneNum) values(#{username}, #{email}, #{password}, #{phoneNum})
    </insert>

    <delete id="delete" parameterType="long">
        delete from sys_user where id=#{uid}
    </delete>
</mapper>
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

UserRoleMapper.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.qfedu.mapper.UserRoleMapper">
    <insert id="save">
        insert into sys_user_role values(#{uid}, #{rid})
    </insert>

    <delete id="delete" parameterType="long">
        delete from sys_user_role where userId=#{uid}
    </delete>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12
13

# 1.5、创建JDBC配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
1
2
3
4

# 1.6、创建核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 引入外部配置文件 -->
    <properties resource="jdbc.properties" />

    <!-- 配置别名 -->
    <typeAliases>
        <package name="com.qfedu.bean" />
    </typeAliases>
    
    <!-- 配置环境 -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </dataSource>
        </environment>
    </environments>

    <!-- 加载映射配置文件 -->
    <mappers>
        <package name="com.qfedu.mapper" />
    </mappers>
</configuration>
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

# 1.7、日志配置文件

log4j.properties

#
# Hibernate, Relational Persistence for Idiomatic Java
#
# License: GNU Lesser General Public License (LGPL), version 2.1 or later.
# See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
#

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=debug, stdout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 1.8、测试

@Test
public void testMyBatis() throws IOException {
    InputStream inputStream = Resources.getResourceAsStream("sqlMapConfig.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession session = sqlSessionFactory.openSession();

    UserMapper userMapper = session.getMapper(UserMapper.class);
    List<User> users = userMapper.list();

    for (User user : users) {
        System.out.println(user);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 二、Spring整合MyBatis 重点

重要步骤:1 配置数据源

2 配置会话工厂

3 配置包扫描

# 2.1、整合思路

将Session工厂(SqlSessionFactory)交给Spring容器进行管理;

# 2.2、将Session工厂(SqlSessionFactory)交给Spring进行管理

<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties" />

<!-- 配置数据源 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</bean>

<!-- 配置sqlSessionFactory,整合MyBatis   SqlSessionFactoryBean -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!-- 配置连接池 -->
    <property name="dataSource" ref="dataSource" />
    <!-- 配置别名 -->
    <property name="typeAliasesPackage" value="com.qfedu.bean" />
    <!-- 配置MyBatis的核心配置文件 -->
    <property name="configLocation" value="classpath:mybatis-config.xml" />
</bean>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 2.3、配置包扫描,生成Mapper接口的动态代理对象

<!-- 配置包扫描,生成Mapper接口的动态代理对象 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.qfedu.mapper" />
</bean>
1
2
3
4

# 2.4、修改MyBatis核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
        <!-- 打印查询语句 -->
        <setting name="logImpl" value="LOG4J" />
    </settings>
</configuration>
1
2
3
4
5
6
7
8

# 2.5、Service层代码

接口RoleService.java

public interface RoleService {
    List<Role> list();

    void save(Role role);
}
1
2
3
4
5

接口UserService.java

public interface UserService {
    List<User> list();
    void save(User user, Long[] roleId);
    void delete(Long uid);
}
1
2
3
4
5

实现类RoleServiceImpl.java


public class RoleServiceImpl implements RoleService {
    private RoleMapper roleMapper;

    public void setRoleMapper(RoleMapper roleMapper) {
        this.roleMapper = roleMapper;
    }

    @Override
    public List<Role> list() {
        return roleMapper.list();
    }

    @Override
    public void save(Role role) {
        roleMapper.save(role);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

实现类UserServiceImpl.java


public class UserServiceImpl implements UserService {
    private UserMapper userMapper;
    private UserRoleMapper userRoleMapper;

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }
    
    public void setUserRoleMapper(UserRoleMapper userRoleMapper) {
        this.userRoleMapper = userRoleMapper;
    }

    @Override
    public List<User> list() {
        List<User> users = userMapper.list();
        return users;
    }

    @Override
    public void save(User user, Long[] roleId) {
        userMapper.save(user);

        Long uid = user.getId();

        for (Long rid : roleId) {
            userRoleMapper.save(uid, rid);
        }
    }

    @Override
    public void delete(Long uid) {
        userRoleMapper.delete(uid);
        userMapper.delete(uid);
    }
}
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

在Spring的核心配置文件中配置Service。

<!-- 创建Service层对象 -->
<bean id="userService" class="com.qfedu.service.impl.UserServiceImpl">
    <property name="userMapper" ref="userMapper" />
    <property name="userRoleMapper" ref="userRoleMapper" />
</bean>
1
2
3
4
5

# 2.6、测试

public class MyTest {
    @Test
    public void testMyBatis() throws IOException {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession session = sqlSessionFactory.openSession();

        UserMapper userMapper = session.getMapper(UserMapper.class);
        List<User> users = userMapper.list();

        for (User user : users) {
            System.out.println(user);
        }
    }

    @Test
    public void testSpringMyBatis() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService = (UserService)context.getBean("userService");
        List<User> userList = userService.list();
        for (User user : userList) {
            System.out.println(user);
        }
    }
}
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

# 三、事务处理

# 3.1、基础工程搭建

# 3.1.1、建库建表

CREATE TABLE `account`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30),
  `money` int(11) NULL DEFAULT NULL, 
  PRIMARY KEY (`id`)
);

INSERT INTO `account` VALUES (1, 'tom', 1000);
INSERT INTO `account` VALUES (2, 'bob', 1000);
1
2
3
4
5
6
7
8
9

# 3.1.2、创建实体类

Account.java

public class Account  {
    private Integer id;
    private String name;
    private Integer money;
    //set、get
    //toString
}
1
2
3
4
5
6
7

# 3.1.3、创建接口及实现类

AccountMapper.java

public interface AccountMapper {
    void update(Account account) throws SQLException;
    Account findById(Integer srcId);
}
1
2
3
4

AccountService.java

public interface AccountService {
    void transfer(Integer srcId, Integer dstId, Integer money) throws Exception;
}
1
2
3

AccountServiceImpl.java

public class AccountServiceImpl implements AccountService {
    private AccountMapper accountMapper;

    public void setAccountMapper(AccountMapper accountMapper) {
        this.accountMapper = accountMapper;
    }

    @Override
    public void transfer(Integer srcId, Integer dstId, Integer money) throws Exception {
        Account src = accountMapper.findById(srcId);
        Account dst = accountMapper.findById(dstId);

        if(src == null) {
            throw new RuntimeException("转出用户不存在");
        }

        if(dst == null) {
            throw new RuntimeException("转入用户不存在");
        }

        if(src.getMoney() < money) {
            throw new RuntimeException("转出账户余额不足");
        }

        src.setMoney(src.getMoney() - money);
        dst.setMoney(dst.getMoney() + money);

        accountMapper.update(src);
        //int x = 1/0;
        accountMapper.update(dst);
    }
}
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

# 3.1.4、创建映射配置文件

<?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.qfedu.mapper.AccountMapper">
    <update id="update" parameterType="account">
        update account set money=#{money} where id=#{id}
    </update>
    <select id="findById" resultType="account">
        select * from account where id=#{id}
    </select>
</mapper>
1
2
3
4
5
6
7
8
9
10
11
12

# 3.1.5、创建JDBC配置文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_mybatis?useSSL=false&useUnicode=true&characterEncoding=utf8
jdbc.username=root
jdbc.password=root
1
2
3
4

# 3.1.6、创建核心配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 加载配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!-- 创建Service层对象 -->
    <bean id="accountService" class="com.qfedu.service.impl.AccountServiceImpl">
        <property name="accountMapper" ref="accountMapper" />
    </bean>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- 配置sqlSessionFactory,整合MyBatis -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 配置别名 -->
        <property name="typeAliasesPackage" value="com.qfedu.bean" />
        <property name="configLocation" value="classpath:mybatis-config.xml" />
    </bean>

    <!-- 配置包扫描 -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.qfedu.mapper" />
    </bean>
</beans>
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

# 3.1.7、基础工程测试

public class MyTest {
    @Test
    public void testTrans() throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = (AccountService)context.getBean("accountService");

        accountService.transfer(1, 2, 100);
    }
}
1
2
3
4
5
6
7
8
9

在转账中间人为制造错误发现无法回滚。

# 3.2、编程式事务控制相关对象 了解

PlatformTransactionManager接口是Spring的事务管理器,它里面提供了我们常用的操作事务的方法

方法 说明
TransactionStatus getTransaction(TransactionDefinition var1) 获取事务状态信息
void commit(TransactionStatus var1) 提交事务
void rollback(TransactionStatus var1) 回滚事务

PlatformTransactionManager是接口类型,不同的Dao层技术则有不同的实现类,Dao层技术是jdbc或mybatis时:org.springframework.jdbc.datasource.DataSourceTransactionManager。

TransactionDefinition是事务的定义信息对象

方法 说明
int getIsolationLevel() 获得事务的隔离级别
int getPropagationBehavior() 事务的传播行为
int getTimeout() 获得超时时间
boolean isReadOnly() 是否只读

# 1. 事务隔离级别

设置隔离级别,可以解决事务并发产生的问题,如脏读、不可重复读和虚读。

  • ISOLATION_DEFAULT
  • ISOLATION_READ_UNCOMMITTED
  • ISOLATION_READ_COMMITTED
  • ISOLATION_REPEATABLE_READ 可重复读
  • ISOLATION_SERIALIZABLE

# 2. 事务传播行为

  • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
  • SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
  • MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
  • REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
  • NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
  • NEVER:以非事务方式运行,如果当前存在事务,抛出异常
  • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行 REQUIRED 类似的操作
  • 超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置
  • 是否只读:建议查询时设置为只读

TransactionStatus接口提供的是事务具体的运行状态

方法 说明
boolean hasSavepoint() 是否存储回滚点
boolean isCompleted() 事务是否完成
boolean isNewTransaction() 是否是新事务
boolean isRollbackOnly() 事务是否回滚

# 3.3、声明式事务控制 重点

# 3.3.1、什么是声明式事务控制

Spring的声明式事务顾名思义就是采用声明的方式来处理事务。这里所说的声明,就是指在配置文件中声明,用在Spring 配置文件中声明式的处理事务来代替代码式的处理事务。

声明式事务处理的作用

  • 事务管理不侵入开发的组件。具体来说,业务逻辑对象就不会意识到正在事务管理之中,事实上也应该如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策划的话,也只需要在定义文件中重新配置即可
  • 在不需要事务管理的时候,只要在设定文件上修改一下,即可移去事务管理服务,无需改变代码重新编译,这样维护起来极其方便

注意:Spring 声明式事务控制底层就是AOP。

# 3.3.2、声明式事务控制实现

声明式事务控制需要明确的几个问题:

  • 谁是切点?
  • 谁是通知?
  • 配置切面?

代码实现如下:

# 3.3.3、引入相关依赖

在基础工程pom.xml中增加如下的依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>
1
2
3
4
5
6
7
8
9
10

# 3.3.4、引入相关命名空间

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 3.3.5、配置事务增强

<!-- 配置平台事务管理器   DataSourceTransactionManager -->
<bean id="transactionManager"     class="org.springframework.jdbc.datasource.DataSourceTransactionManager" >
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 通知,事务的增强   让事务管理器生效-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*"    />
    </tx:attributes>
</tx:advice>
1
2
3
4
5
6
7
8
9
10
11

# 3.3.6、配置织入

<!-- 配置切面 -->
<aop:config>
    <aop:pointcut id="pt" expression="execution(* com.qf.service..*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt" />
</aop:config>
1
2
3
4
5

# 3.3.7、测试

public class MyTest {
    @Test
    public void testTrans() throws Exception {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = (AccountService)context.getBean("accountService");

        accountService.transfer(1, 2, 100);
    }
}
1
2
3
4
5
6
7
8
9
上次更新: 2024/4/13