Spring整合MyBatis与事务管理详解(第三部分)

十、Spring整合Mybatis

1. pom.xml依赖配置

复制代码
<!-- Spring核心 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.23</version>
</dependency>

<!-- Spring AOP支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.23</version>
</dependency>

<!-- 数据库驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</dependency>

<!-- Druid连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.16</version>
</dependency>

<!-- MyBatis-Spring整合包 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.1.0</version>
</dependency>

<!-- 日志 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.36</version>
</dependency>

<!-- 测试 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.3.23</version>
    <scope>test</scope>
</dependency>

2. log4j.properties

复制代码
# 设置根日志级别为INFO,输出到控制台
log4j.rootLogger=INFO, stdout

# 控制台输出配置
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# MyBatis日志输出级别
log4j.logger.com.hg.mapper=DEBUG

3. 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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1. IOC注解扫描 -->
    <context:component-scan base-package="com.hg"></context:component-scan>

    <!-- 2. 引入外部属性文件 -->
    <context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
    
    <!-- 3. 数据源配置 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    
    <!-- 4. SqlSessionFactory配置 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 5. Mapper接口扫描配置 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.hg.mapper"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>
</beans>

4. db.properties数据库配置

复制代码
jdbc.driverClass=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456

5. Spring整合JUnit测试

复制代码
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringMybatisTest {

    @Autowired
    private UserService userService;
    
    @Test
    public void testGetUser() {
        User user = userService.getUserById(1L);
        System.out.println(user);
    }
    
    // 其他测试方法...
}

十一、事务介绍

1. 什么是事务?

事务是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作。这些操作要么全部执行,要么全部不执行。

核心思想:执行多条SQL语句,要么全部成功,要么全部回滚。

2. 事务的特点(ACID属性)

属性 说明 示例
原子性 事务是最小的执行单位,不可再分割 转账操作:扣款和加款必须同时成功或失败
一致性 事务执行前后,数据完整性不被破坏 转账前后,两个账户总金额不变
隔离性 多个事务并发执行时互不干扰 事务A读取数据时,事务B不能修改该数据
持久性 事务一旦提交,对数据库的改变是永久的 转账成功后,即使系统崩溃,数据也不会丢失

3. MySQL控制事务

复制代码
-- 开启事务
START TRANSACTION;

-- 执行SQL语句
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;

-- 提交事务(持久化到数据库)
COMMIT;

-- 或回滚事务(撤销所有操作)
ROLLBACK;

工作原理

  • START TRANSACTION:创建undo_log记录老数据

  • COMMIT:删除undo_log,提交更改

  • ROLLBACK:根据undo_log恢复老数据

4. JDBC控制事务

复制代码
Connection conn = null;
try {
    // 获取数据库连接
    conn = DriverManager.getConnection(url, username, password);
    
    // 关闭自动提交,相当于START TRANSACTION
    conn.setAutoCommit(false);
    
    // 执行SQL操作
    statement1.executeUpdate("UPDATE account SET balance = balance - 100 WHERE id = 1");
    statement2.executeUpdate("UPDATE account SET balance = balance + 100 WHERE id = 2");
    
    // 提交事务
    conn.commit();
} catch (Exception e) {
    // 回滚事务
    if (conn != null) {
        conn.rollback();
    }
    e.printStackTrace();
} finally {
    // 关闭资源
    if (conn != null) {
        conn.close();
    }
}

5. MyBatis控制事务

复制代码
SqlSession sqlSession = null;
try {
    // 获取SqlSession,参数false表示关闭自动提交
    sqlSession = sqlSessionFactory.openSession(false);
    
    // 获取Mapper
    AccountMapper mapper = sqlSession.getMapper(AccountMapper.class);
    
    // 执行操作
    mapper.deduct(1L, 100);
    mapper.add(2L, 100);
    
    // 提交事务
    sqlSession.commit();
} catch (Exception e) {
    // 回滚事务
    if (sqlSession != null) {
        sqlSession.rollback();
    }
    e.printStackTrace();
} finally {
    // 关闭SqlSession
    if (sqlSession != null) {
        sqlSession.close();
    }
}

十二、Spring事务控制的API

1. PlatformTransactionManager(平台事务管理器)

作用:Spring事务管理的核心接口,负责事务的开启、提交和回滚。

常用实现类

  • DataSourceTransactionManager:适用于JDBC和MyBatis

  • HibernateTransactionManager:适用于Hibernate

  • JpaTransactionManager:适用于JPA

配置示例

复制代码
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

2. TransactionDefinition(事务定义)

作用:定义事务的属性,包括隔离级别、传播行为、超时时间等。

常用属性

(1)隔离级别(Isolation Level)
隔离级别 脏读 不可重复读 幻读 性能
READ_UNCOMMITTED 最高
READ_COMMITTED
REPEATABLE_READ
SERIALIZABLE 最低
  • 默认值:使用底层数据库的默认隔离级别

  • MySQL默认:REPEATABLE_READ

  • Oracle默认:READ_COMMITTED

(2)传播行为(Propagation Behavior)
传播行为 说明 适用情况
REQUIRED(默认) 如果当前存在事务,则加入该事务;如果不存在,则新建一个事务 增删改操作
SUPPORTS 如果当前存在事务,则加入该事务;如果不存在,则以非事务方式运行 查询操作
REQUIRES_NEW 新建事务,如果当前存在事务,则挂起当前事务 独立事务操作
NOT_SUPPORTED 以非事务方式运行,如果当前存在事务,则挂起当前事务 不涉及事务的操作
NEVER 以非事务方式运行,如果当前存在事务,则抛出异常 禁止在事务中运行
MANDATORY 必须在一个已有的事务中运行,否则抛出异常 强制要求事务
NESTED 如果当前存在事务,则在嵌套事务内执行 复杂的业务场景
(3)其他属性
属性 说明 默认值
超时时间 事务最长执行时间(秒),超时则自动回滚 -1(永不超时)
是否只读 标记为只读事务,优化查询性能 false
回滚规则 指定哪些异常触发回滚 RuntimeException和Error

3. TransactionStatus(事务状态)

作用:表示事务的当前状态,可以用于控制事务的提交和回滚。

常用方法

复制代码
public interface TransactionStatus extends SavepointManager {
    // 判断是否有保存点
    boolean hasSavepoint();
    
    // 判断是否是新事务
    boolean isNewTransaction();
    
    // 设置只回滚
    void setRollbackOnly();
    
    // 判断是否标记为只回滚
    boolean isRollbackOnly();
    
    // 刷新事务
    void flush();
    
    // 判断事务是否已完成
    boolean isCompleted();
}

十三、Spring声明式事务管理配置

1. 添加事务管理依赖

复制代码
<!-- Spring事务管理 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.3.23</version>
</dependency>

<!-- Spring JDBC支持 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.23</version>
</dependency>

2. 完整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: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/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       https://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 1. IOC注解扫描 -->
    <context:component-scan base-package="com.hg"></context:component-scan>

    <!-- 2. 引入外部属性文件 -->
    <context:property-placeholder location="classpath:db.properties"/>

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

    <!-- 4. SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 5. Mapper接口扫描 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.hg.mapper"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

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

    <!-- 7. 开启注解驱动的事务管理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

3. Service层事务使用示例

复制代码
@Service
@Transactional // 类级别注解,所有public方法都应用事务
public class UserServiceImpl implements UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    // 查询方法使用只读事务,提高性能
    @Override
    @Transactional(readOnly = true)
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }
    
    // 转账操作,需要完整的事务控制
    @Override
    @Transactional(
        propagation = Propagation.REQUIRED,  // 传播行为:有事务就加入,没有就新建
        isolation = Isolation.DEFAULT,       // 隔离级别:使用数据库默认
        timeout = 30,                        // 超时时间:30秒
        rollbackFor = Exception.class        // 回滚规则:所有异常都回滚
    )
    public void transferMoney(Long fromId, Long toId, BigDecimal amount) {
        // 1. 扣款
        User fromUser = userMapper.selectById(fromId);
        fromUser.setBalance(fromUser.getBalance().subtract(amount));
        userMapper.update(fromUser);
        
        // 2. 加款
        User toUser = userMapper.selectById(toId);
        toUser.setBalance(toUser.getBalance().add(amount));
        userMapper.update(toUser);
        
        // 如果发生异常,Spring会自动回滚上述两个操作
    }
    
    // 支持当前事务,如果没有事务则以非事务运行
    @Override
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<User> getAllUsers() {
        return userMapper.selectAll();
    }
}

4. 事务传播行为示例

复制代码
@Service
public class OrderService {
    
    @Autowired
    private UserService userService;
    
    @Transactional
    public void createOrder(Order order) {
        // 1. 保存订单
        orderMapper.insert(order);
        
        // 2. 扣减库存(在同一个事务中)
        productService.deductStock(order.getProductId(), order.getQuantity());
        
        // 3. 记录日志(新建独立事务)
        logService.addLog("创建订单", order.getId());
    }
}

@Service
public class ProductService {
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void deductStock(Long productId, Integer quantity) {
        // 这个方法会加入createOrder的事务
        productMapper.updateStock(productId, quantity);
    }
}

@Service
public class LogService {
    
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void addLog(String operation, Long targetId) {
        // 这个方法会新建事务,不受外层事务影响
        logMapper.insert(new Log(operation, targetId));
    }
}

总结

通过Spring整合MyBatis和配置声明式事务管理,我们可以:

  1. 简化配置:通过注解和XML配置,减少模板代码

  2. 统一管理:集中管理数据库连接和事务配置

  3. 提高效率:利用AOP实现非侵入式的事务管理

  4. 增强维护性:事务配置与业务代码分离,便于维护

  5. 保证一致性:确保业务操作的原子性和一致性

这种整合方式是企业级Java开发的标配,能够有效提高开发效率和系统稳定性。

相关推荐
没有bug.的程序员2 小时前
500个微服务上云全线假死:Spring Boot 3.2 自动配置底层的生死狙击
java·spring boot·微服务·kubernetes·自动配置
sinat_255487812 小时前
保存 Object 数组
java·服务器·前端
myloveasuka2 小时前
Object&Objects
java·开发语言
Java&Develop2 小时前
查看 maven详细报错 打包
java
sibylyue2 小时前
JDK 17 +spiring boot+ maven 应用服务 高并发调优
java·开发语言·maven
NE_STOP2 小时前
SpringCloud--快速上手Eureka注册中心
spring
老友@2 小时前
微服务全面解析:架构、组件与底层原理
数据库·spring·oracle
艾莉丝努力练剑2 小时前
确保多进程命名管道权限一致的方法
java·linux·运维·服务器·开发语言·网络·c++
tiany5242 小时前
养虾记录-如何配置多agent和多个飞书机器人独立对话
java·前端·飞书