本文主要介绍Spring中的事务相关知识:
1、熟悉事务管理的三个核心接口
2、了解Spring事务的两种方式
3、掌握基于XML和注解的事务使用
1、Spring事务管理概述
1、事务管理的核心接口
1、PlatformTransactionManager
PlatformTransactionManage接口是Spring平台提供的平台事务管理器,主要用于管理事务。该接口中主要包含3个事务操作方法。
- TransactionStatus getTransaction(TransactionDefinition definition):根据事务定义信息从事务环境中返回一个已存在的事务,或者创建一个新的事务。
- void commit(TransactionStatus status):根据事务的状态提交事务,如果事务状态已经标识为 rollback-only,该方法执行回滚事务的操作。
- void rollback(TransactionStatus status):将事务回滚,当 commit 方法抛出异常时,rollback 会被隐式调用。
2、使用事务选用实现类
- JDBC 和 MyBatis 使用 DataSourceTransactionManager。
- Hibernate 使用 HibernateTransactionManager。
- JPA 使用 JpaTransactionManager。
3、TransactionDefinition接口
- int getPropagationBehavior():获取事务的传播行为。
- int getIsolationLevel();:获取事务的隔离级别。
- int getTimeout();:获取事务的超时时间。
- boolean isReadOnly();:判断事务是否只读。
- String getName();:获取事务对象名称。
4、TransactionStatus
- boolean isNewTransaction(); 判断是否是新的事务
- boolean hasSavepoint(); 判断是否存在保存点
- void setRollbackOnly(); 设置事务回滚
- boolean isRollbackOnly(); 判断是否回滚
- void flush(); 刷新事务
- boolean isCompleted(); 判断事务是否完成
2、事务管理的方式
- 编程式事务:通过编写代码来管理事务;
- 声明式事务:通过XML配置或注解来管理事务。
2、基于XML方式的声明式事务
1、创建一个maven项目导入相关依赖
<dependencies>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
<scope>runtime</scope>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<!-- MyBatis 相关 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- Spring 集成 MyBatis 的依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--控制台日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<!-- Spring 相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.8.RELEASE</version>
</dependency>
<!-- 测试相关 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.8.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
</dependencies>
2、准备数据库
CREATE TABLE `account` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`balance` decimal(10, 0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `account` VALUES (1, 10000);
INSERT INTO `account` VALUES (2, 0);
3、编写dao接口及Mapper.xml文件
- dao接口
package cn.simplelife.mapper;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
/**
* @ClassName AccountMapper
* @Description
* @Author simplelife
* @Date 2022/11/23 15:34
* @Version 1.0
*/
public interface AccountMapper {
void addBalance(@Param("inId") Long inId, @Param("amount") BigDecimal amount);
void subtractBalance(@Param("outId") Long outId, @Param("amount") BigDecimal amount);
}
- mapper.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.simplelife.mapper.AccountMapper">
<update id="addBalance">
UPDATE account
SET balance=balance + #{amount}
WHERE id = #{inId}
</update>
<update id="subtractBalance">
UPDATE account
SET balance=balance - #{amount}
WHERE id = #{outId}
</update>
</mapper>
4、编写实体类
package cn.simplelife.domain;
import lombok.Data;
import java.math.BigDecimal;
/**
* @ClassName Account
* @Description
* @Author simplelife
* @Date 2022/11/23 15:15
* @Version 1.0
*/
@Data
public class Account {
private Long id;
private BigDecimal balance;
}
5、编写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>
<!--配置别名-->
<typeAliases>
<package name="cn.simplelife.domain"/>
</typeAliases>
<!--关联mapper-->
<mappers>
<mapper resource="cn/simplelife/mapper/*.xml"/>
</mappers>
</configuration>
6、编写业务接口及实现类
- 业务层接口
package cn.simplelife.service;
import java.math.BigDecimal;
/**
* @ClassName IAccountService
* @Description
* @Author simplelife
* @Date 2022/11/23 17:09
* @Version 1.0
*/
public interface IAccountService {
void transfer(Long outId, Long inId, BigDecimal amount);
}
- 业务层实现类
package cn.simplelife.service.impl;
import cn.simplelife.mapper.AccountMapper;
import cn.simplelife.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
/**
* @ClassName IAccountServiceImpl
* @Description
* @Author simplelife
* @Date 2022/11/23 17:10
* @Version 1.0
*/
@Service
public class IAccountServiceImpl implements IAccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public void transfer(Long outId, Long inId, BigDecimal amount) {
accountMapper.subtractBalance(outId, amount);
System.out.println(10 / 0);
accountMapper.addBalance(inId, amount);
}
}
7、编写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"
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">
<!--配置Ioc 和 Di注解解析器-->
<context:component-scan base-package="cn.simplelife.*"/>
<!-- 关联db.properties-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${password}"/>
</bean>
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.simplelife.mapper"/>
</bean>
<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>
<!--配置增强 WHEN WHAT-->
<tx:advice id="txAdvice" transaction-manager="dataSourceTransactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!--配置切入点 WHERE-->
<aop:pointcut id="txPoint" expression="execution(* cn.simplelife.service.impl.*ServiceImpl.*(..))"/>
<!--关联三者-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
</beans>
8、编写测试类
import cn.simplelife.service.IAccountService;
import cn.simplelife.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.math.BigDecimal;
/**
* @ClassName SqlSessionFactoryTest
* @Description
* @Author simplelife
* @Date 2022/11/23 15:26
* @Version 1.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SqlSessionFactoryTest {
@Autowired
private IAccountService iAccountService;
@Test
public void getSqlSessionTest() {
System.out.println(MybatisUtils.getSqlSession());
iAccountService.transfer(1L, 2L, new BigDecimal("100"));
}
}
9、测试结果
- 成功
- 失败
3、基于注解的事务
1、修改配置文件如下
<?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"
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">
<!--配置Ioc 和 Di注解解析器-->
<context:component-scan base-package="cn.simplelife.*"/>
<!-- 关联db.properties-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置数据源-->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${password}"/>
</bean>
<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="druidDataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.simplelife.mapper"/>
</bean>
<!--配置事务管理器-->
<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>
<!--开启事务注解支持-->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
</beans>
2、修改业务层方法
添加注解
package cn.simplelife.service.impl;
import cn.simplelife.mapper.AccountMapper;
import cn.simplelife.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
/**
* @ClassName IAccountServiceImpl
* @Description
* @Author simplelife
* @Date 2022/11/23 17:10
* @Version 1.0
*/
@Service
public class IAccountServiceImpl implements IAccountService {
@Autowired
private AccountMapper accountMapper;
@Override
@Transactional
public void transfer(Long outId, Long inId, BigDecimal amount) {
accountMapper.subtractBalance(outId, amount);
System.out.println(10 / 0);
accountMapper.addBalance(inId, amount);
}
}