AI 摘要

文章系统讲解 Spring 与 MyBatis 的轻量级整合方案:先以 IoC 接管 MyBatis 生命周期,通过 SqlSessionFactoryBean、SqlSessionTemplate 实现 DAO;再用 MapperScannerConfigurer 自动扫描接口生成实现,简化编码。随后引入声明式事务,对比编程式痛点,给出 XML 及 @Transactional 注解两种配置,演示传播行为、隔离级别、回滚规则等核心属性,并附完整用户表增删改查示例与测试,帮助读者快速构建低耦合、高可维护的 SSM 企业应用。

基本整合方式

整合思路梳理

Spring 整合其他框架的本质其实就是把其他框架交给 Spring 管理,Spring 通过 IoC、AOP 等机制实现与其他框架的连接,最终建立一个低耦合的应用架构。

MyBatis 三大核心接口和类的关系:

使用 Spring 整合 MyBatis 时,以上流程可以全部移交给 Spring 来处理,即读取配置文件、组件的创建、组件之间的依赖关系以及整个框架的生命周期都由 Spring 容器统一管理。

初步整合方式

Spring 和 MyBatis 的整合步骤:

  • 导入 JAR 依赖包:Spring IoC/AOP 依赖、事务依赖、dbcp 数据源依赖、MyBatis 依赖、Spring 与 MyBatis 整合依赖。
  • 编写配置文件:MyBatis 核心配置文件、Spring 核心配置文件。
  • 通过 Spring 配置文件配置数据源,创建数据源对象。
  • 通过 Spring 配置文件配置 SqlSessionFactory,创建 SqlSessionFactory 对象。
  • 通过 Spring 配置文件配置 SqlSession,创建 SqlSession 对象。
  • 通过 SqlSession 操作数据库。

示例:Spring 与 MyBatis 初步整合

SQL 脚本:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_address
-- ----------------------------
DROP TABLE IF EXISTS `t_address`;
CREATE TABLE `t_address` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `contact` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '联系人姓名',
  `addressDesc` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '收货地址明细',
  `postCode` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '邮编',
  `tel` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '联系人电话',
  `userId` bigint DEFAULT NULL COMMENT '用户id',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='地址表';

-- ----------------------------
-- Records of t_address
-- ----------------------------
BEGIN;
INSERT INTO `t_address` (`id`, `contact`, `addressDesc`, `postCode`, `tel`, `userId`) VALUES (1, '张三', '福建省泉州市安溪县宝龙城市广场', '362400', '13275000001', 1);
INSERT INTO `t_address` (`id`, `contact`, `addressDesc`, `postCode`, `tel`, `userId`) VALUES (2, '张三', '福建省厦门市集美区软件园3期', '361000', '13275000002', 1);
COMMIT;

-- ----------------------------
-- Table structure for t_sys_role
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_role`;
CREATE TABLE `t_sys_role` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `code` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色编码',
  `roleName` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '角色名称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='角色表';

-- ----------------------------
-- Records of t_sys_role
-- ----------------------------
BEGIN;
INSERT INTO `t_sys_role` (`id`, `code`, `roleName`) VALUES (1, 'SYS_ADMIN', '管理员');
INSERT INTO `t_sys_role` (`id`, `code`, `roleName`) VALUES (2, 'SYS_MANAGER', '店长');
INSERT INTO `t_sys_role` (`id`, `code`, `roleName`) VALUES (3, 'SYS_EMPLOYEE', '店员');
COMMIT;

-- ----------------------------
-- Table structure for t_sys_user
-- ----------------------------
DROP TABLE IF EXISTS `t_sys_user`;
CREATE TABLE `t_sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `account` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '账号',
  `realName` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '真实姓名',
  `password` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '密码',
  `sex` int DEFAULT NULL COMMENT '性别,1女/2男',
  `birthday` date DEFAULT NULL COMMENT '出生日期',
  `phone` varchar(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '手机号码',
  `roleId` bigint DEFAULT NULL COMMENT '用户角色id',
  `certFile` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '证件照片',
  `avatarFile` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '工作证照片',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';

-- ----------------------------
-- Records of t_sys_user
-- ----------------------------
BEGIN;
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (1, 'admin', 'admin', '0000000', 1, '1980-11-01', '13888888888', 1, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (2, 'limingxing', '李明星', '0000000', 2, '1983-12-10', '13188888888', 2, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (3, 'wanglulu', '王璐璐', '0000000', 2, '1984-06-05', '15766666666', 2, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (4, 'hauzhiqing', '华志强', '0000000', 1, '1983-06-15', '15866666666', 3, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (5, 'huangweihua', '黄卫华', '0000000', 2, '1982-12-31', '13800099999', 3, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (6, 'zhaogang', '赵刚', '0000000', 2, '1980-01-01', '15266655555', 2, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (7, 'liuyang', '刘阳', '0000000', 2, '1978-03-12', '17099999999', 2, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (8, 'lijiangtao', '李江涛', '0000000', 1, '1983-12-10', '13277777777', 3, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (9, 'liuzhong', '刘忠', '0000000', 2, '1981-11-04', '15388888888', 3, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (10, 'zhangfeng', '张峰', '0000000', 2, '1980-01-01', '19288899999', 3, NULL, NULL);
INSERT INTO `t_sys_user` (`id`, `account`, `realName`, `password`, `sex`, `birthday`, `phone`, `roleId`, `certFile`, `avatarFile`) VALUES (11, 'songke', '宋科', '0000000', 1, '1983-10-10', '18900000000', 2, NULL, NULL);
COMMIT;

SET FOREIGN_KEY_CHECKS = 1;

导入相关依赖 JAR 文件,并添加为库。

示例效果:

用户表实体类(cn.duozai.entity.SysUser):

public class SysUser implements Serializable {

    /**
     * 主键id
     */
    private Integer id;

    /**
     * 账号
     */
    private String account;

    /**
     * 真实姓名
     */
    private String realName;

    /**
     * 密码
     */
    private String password;

    /**
     * 性别,1女/2男
     */
    private Integer sex;

    /**
     * 出生日期
     */
    private Date birthday;

    /**
     * 手机号码
     */
    private String phone;

    /**
     * 用户角色id
     */
    private Integer roleId;

    /**
     * 证件照片
     */
    private String certFile;

    /**
     * 工作证照片
     */
    private String avatarFile;

    // ...

}

用户表数据访问接口(cn.duozai.dao.SysUserMapper):

public interface SysUserMapper {

    /**
     * 查询用户列表
     *
     * @return 用户列表
     */
    List<SysUser> getUserList();

}

用户表数据访问接口 SQL 映射文件(cn/duozai/dao/SysUserMapper.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="cn.duozai.dao.SysUserMapper">
    <select id="getUserList" resultType="cn.duozai.entity.SysUser">
        SELECT * FROM t_sys_user
    </select>
</mapper>

Log4J 配置文件(classpath:log4j.properties):

log4j.rootLogger=debug,CONSOLE

##################
# Console Appender
##################
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=debug
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern= - (%r ms) - %d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n

MyBatis 核心配置文件(classpath:mybatis-config.xml):

<?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>
    <!-- 1、配置MyBatis框架运行时行为 -->
    <settings>
        <!-- 配置日志实现 -->
        <setting name="logImpl" value="LOG4J"/>
        <!-- 配置自动映射级别 -->
        <setting name="autoMappingBehavior" value="FULL"/>
    </settings>

    <!-- 2、配置别名扫包 -->
    <typeAliases>
        <package name="cn.duozai.entity"/>
    </typeAliases>

    <!-- 3、配置MyBatis框架运行环境-配置MySQL数据源:交给Spring操作 -->

    <!-- 4、配置SQL映射文件 -->
    <mappers>
        <package name="cn.duozai.dao"/>
    </mappers>
</configuration>

Spring 核心配置文件(classpath:applicationContext.xml):

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

    <!-- 开启Spring IoC注解 -->
    <context:component-scan base-package="cn.duozai"/>
    <!-- 开启Spring AOP注解 -->
    <aop:aspectj-autoproxy/>

    <!-- SM整合1-创建dbcp数据源:取代MyBatis的dataSource配置项 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <!-- 设值注入数据库配置,创建数据源Bean对象 -->
        <!-- XML中注意特殊符号的处理,url中的“&”号 -->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://127.0.0.1:3306/cvs_db?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456.."/>
    </bean>

    <!-- SM整合2-创建SqlSessionFactory:取代SqlSessionFactoryBuilder创建工厂 -->
    <!-- SqlSessionFactoryBean:关联数据源并创建SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 注入dbcp数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 注入MyBatis核心配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <!-- MyBatis其他核心配置项也可以在这里配置 -->
    </bean>

    <!-- SM整合3-创建SqlSession:使用更高级的SqlSessionTemplate代替SqlSession -->
    <!-- SqlSessionTemplate:关联sqlSessionFactory创建会话对象 -->
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <!-- 构造注入sqlSessionFactory工厂 -->
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

用户表数据访问接口实现类(cn.duozai.dao.SysUserMapperImpl):

@Repository
public class SysUserMapperImpl implements SysUserMapper {

    /**
     * 从Spring容器中注入SqlSessionTemplate
     */
    @Resource(name = "sqlSessionTemplate")
    SqlSessionTemplate sqlSession;

    /**
     * 查询用户列表
     *
     * @return 用户列表
     */
    @Override
    public List<SysUser> getUserList() {
        // 通过SqlSessionTemplate调用Mapper接口,实现数据库操作
        return sqlSession.getMapper(SysUserMapper.class).getUserList();
    }

}

用户表业务逻辑接口(cn.duozai.dao.SysUserService):

public interface SysUserService {

    /**
     * 查询用户列表
     *
     * @return 用户列表
     */
    List<SysUser> getUserList();

}

用户表业务逻辑接口实现类(cn.duozai.dao.SysUserServiceImpl):

@Service
public class SysUserServiceImpl implements SysUserService {

    /**
     * 从Spring容器中注入依赖的Dao层对象
     */
    @Resource
    SysUserMapper sysUserMapper;

    /**
     * 查询用户列表
     *
     * @return 用户列表
     */
    @Override
    public List<SysUser> getUserList() {
        // Service层调用Dao层操作数据库
        return sysUserMapper.getUserList();
    }

}

测试类(test.SmTest):

public class SmTest {

    private static final Logger logger = Logger.getLogger(SmTest.class);

    /**
     * 查询用户列表
     *
     * @return void
     */
    @Test
    public void getUserList() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        SysUserService sysUserService = (SysUserService) context.getBean("sysUserServiceImpl");
      
        List<SysUser> sysUserList = sysUserService.getUserList();
        for (SysUser sysUser : sysUserList) {
            logger.debug("SysUser => " + sysUser.getRealName());
        }
    }

}

示例效果:


映射器整合方式

mybatis-spring 提供了 MapperScannerConfigurer,可以扫描指定 Dao 包中的接口和 SQL 映射文件,将其注册为映射器,快速生成 Dao 层接口实现类,简化编码。


示例:使用 MapperScannerConfigurer 注入映射器

Spring 核心配置文件(classpath:applicationContext.xml):

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

    <!-- ... -->

    <!-- SM整合3-创建SqlSession:使用更高级的SqlSessionTemplate代替SqlSession -->
    <!-- SqlSessionTemplate:关联sqlSessionFactory创建会话对象 -->
    <!--
    <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    -->

    <!-- SM整合3-使用MapperScannerConfigurer扫包生成Dao层实现类:取代MapperImpl -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="cn.duozai.dao"/>
    </bean>

</beans>

示例效果:


声明式事务

MySQL 事务概述

事务是一组数据库操作的逻辑单元,这些操作要么全部成功执行,要么全部不执行,以确保数据的一致性和完整性。

事务场景:你去银行转账,从一个账户扣款和在另一个账户收款这两个操作应该作为一个整体来完成,要么都成功(转账成功),要么都不发生(转账失败),不能出现钱从一个账户扣了,但另一个账户没收到的情况,这就是事务要保证的效果。

事务的 ACID 特性:

  • 原子性(Atomicity):事务中的所有操作是一个不可分割的整体,就像一个原子一样,要么所有操作都成功完成,事务提交;要么所有操作都失败回滚,回到事务开始前的状态。
  • 一致性(Consistency):事务执行前后,数据库的状态必须保持一致,数据库要满足所有既定的约束条件,如数据类型约束、唯一性约束、外键约束等。
  • 隔离性(Isolation):多个并发执行的事务之间应该相互隔离,互不干扰,隔离性可以防止事务之间出现数据不一致的问题。
  • 持久性(Durability):一旦事务提交成功,其对数据库所做的修改就应该是永久性的,即使后续系统出现故障,如断电、服务器崩溃等,这些修改也不会丢失。

并发场景下,事务引发的问题:

  • 脏读(Dirty Read):当一个事务正在访问数据并且对数据进行了修改,此时还未提交到数据库中,这时另一个事务也访问并使用了这个数据,由于上个事务还未提交,此时读到的就是 “脏数据”,根据 “脏数据” 所做的操作可能是不正确的。
  • 丢失修改(Lost to Modify):当两个或多个事务同时修改同一数据时,后提交的事务可能会覆盖前一个事务所做的修改,导致前一个事务的更新被丢失,这可能会导致数据不一致性或意外的结果。
  • 死锁(Deadlock):两个或多个事务相互等待对方释放资源,导致系统无法继续执行。
  • 不可重复读(Unrepeatable read):一个事务在读取某个数据后,另一个事务修改了该数据并提交。当第一个事务再次读取同一数据时,得到的结果与之前不一致。
  • 幻读(Phantom read):幻读与不可重复读类似,它发生在一个事务读取了几行数据,接着另一个并发事务插入了一些数据时,在随后的查询中,第一个事务就会发现一些原本不存在的记录。

声明式事务概述

在传统方案下,通过在业务方法中硬编码的方式进行事务控制,事务相关代码分散在业务方法中难以重用,复杂事务的编码难度高,增加了开发难度。

Spring 提供了基于 AOP 实现的声明式事务处理机制,所有的工作均在配置文件中完成,将事务处理相关工作与业务代码分离开,极大地降低了开发和维护难度。

配置声明式事务

一般事务要求在做增删改操作时,如果产生异常,应该做事务回滚。

在未配置声明式事务的情况下执行增删改操作,即使产生异常,也不会进行事务回滚,数据的增删改依然生效。


示例:未配置声明式事务时存在的问题

用户表数据访问接口(cn.duozai.dao.SysUserMapper):

public interface SysUserMapper {

    // ...

    /**
     * 根据用户id修改密码
     *
     * @param id 用户id
     * @param newPassword 新密码
     * @return SQL语句影响行数
     */
    int updatePassword(@Param("userId") Integer id, @Param("newPassword") String newPassword);

}

用户表数据访问接口 SQL 映射文件(cn/duozai/dao/SysUserMapper.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="cn.duozai.dao.SysUserMapper">
    <!-- ... -->

    <update id="updatePassword">
        UPDATE t_sys_user SET password = #{newPassword}
        WHERE id = #{userId}
    </update>
</mapper>

用户表业务逻辑接口(cn.duozai.dao.SysUserService):

public interface SysUserService {

    // ...
  
    /**
     * 根据用户id修改密码
     *
     * @param id 用户id
     * @param newPassword 新密码
     * @return 修改结果
     */
    boolean updatePassword(Integer id, String newPassword);

}

用户表业务逻辑接口实现类(cn.duozai.dao.SysUserServiceImpl):

@Service
public class SysUserServiceImpl implements SysUserService {

    @Resource
    SysUserMapper sysUserMapper;

    // ...

    /**
     * 根据用户id修改密码
     *
     * @param id 用户id
     * @param newPassword 新密码
     * @return 修改结果
     */
    @Override
    public boolean updatePassword(Integer id, String newPassword) {
        int result = sysUserMapper.updatePassword(id, newPassword);

        System.out.println(1/0);
        // 抛出异常,应该做事务回滚
        // 如果没有配置声明式事务,则事务不会回滚

        return result > 0;
    }

}

测试类(test.SmTest):

public class SmTest {

    // ...

    /**
     * 根据用户id修改密码
     *
     * @return void
     */
    @Test
    public void updatePassword() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        SysUserService sysUserService = (SysUserService) context.getBean("sysUserServiceImpl");

        boolean result = sysUserService.updatePassword(1, "000111");
        if(result) {
            logger.debug("修改成功");
        } else {
            logger.debug("修改失败");
        }
    }

}

示例效果:


声明式事务关注的核心问题是对哪些方法,采取什么样的事务策略。

配置声明式事务的基本属性:

名称描述
propagation事务传播机制,默认值为 REQUIRED
如果当前存在事务,那么该方法将会在该事务中运行
如果当前没有事务,那么它会启动一个新的事务
isolation事务隔离级别,默认值为 DEFAULT,使用数据库默认的事务隔离级别
timeout事务超时时间,单位为秒,超过给定时间自动回滚
read-only事务是否为只读,默认为 false
对于只执行查询功能的事务,设置为 true 能提高事务处理性能
rollback-for设定能够触发事务回滚的异常类型
no-rollback-for设定不触发回滚的异常类型

示例:使用 XML 配置声明式事务

Spring 核心配置文件(classpath:applicationContext.xml):

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

    <!-- 事务配置1-配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--
        事务配置2-设置事务属性
        <tx:advice>元素节点:设置事务属性
        id属性:事务属性配置名称
        transaction-manager属性:关联的事务管理器,默认值为transactionManager
    -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--
            <tx:attributes>元素节点:设置一组事务属性
            <tx:method>元素节点:针对某一些方法设置事务属性
            name属性:匹配方法名称
            propagation属性:事务传播属性,一般设置为REQUIRED
            isolation属性:事务隔离级别
        -->
        <tx:attributes>
            <tx:method name="update*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>

    <!-- 事务配置2-定义事务切面 -->
    <aop:config>
        <!-- 定义切入点表达式 -->
        <aop:pointcut id="pointcut" expression="execution(* cn.duozai.service..*.*(..))"/>
        <!--
            <aop:advisor>标签:定义事务切面
            advice-ref属性:关联事务属性配置名称
            pointcut-ref属性:关联切入点表达式
        -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>

</beans>

示例效果:


使用注解实现声明式事务

Spring 支持使用注解的方式结合少量配置来实现声明式事务。


示例:使用注解配置声明式事务

Spring 核心配置文件(classpath:applicationContext.xml):

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

    <!-- ... -->

    <!-- 事务配置1-配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 事务配置2-启用事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

用户表业务逻辑接口实现类(cn.duozai.dao.SysUserServiceImpl):

/**
 * @Transactional注解:开启声明式事务
 */
@Transactional
@Service
public class SysUserServiceImpl implements SysUserService {

    // ...

}