欢迎光临
我们一直在努力

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和配置声明式事务管理,我们可以:

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

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

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

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

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

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

    赞(0)
    未经允许不得转载:171主机测评 » Spring整合MyBatis与事务管理详解(第三部分)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址