一、概述
编程式事务是指程序员手动的在业务代码中控制事务执行的流程,业务方法正常执行提交事务,业务方法执行过程中出现异常则回滚事务。
二、编程式事务环境搭建
2.1、项目概览
2.2、pom.xml
<dependencies>
<!--spring基本依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- 数据源 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.27</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.1.0</version>
</dependency>
<!-- 普通maven项目中使用Sl4j注解 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.10</version>
</dependency>
<!-- aop -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
<!-- 工具 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.3</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.22</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
2.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">
<!-- 组件扫描 -->
<context:component-scan base-package="org.star"/>
<!-- 数据源 -->
<context:property-placeholder location="db.properties"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<!--
配置sqlSessionFactory:读取配置文件,获取数据库相关的信息
-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="org.star.entity.model"></property>
<property name="mapperLocations" value="classpath:mapper/*.xml"></property>
<property name="configuration">
<bean class="org.apache.ibatis.session.Configuration">
<property name="logImpl" value="org.apache.ibatis.logging.stdout.StdOutImpl"></property>
</bean>
</property>
</bean>
<!--
配置mapper接口的位置,并指定sqlSessionFactory
sqlSession = sqlSessionFactory.openSession();
mapper = sqlSession.getMapper();
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="org.star.mapper"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
2.4、db.properties
db.driver=com.mysql.cj.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/20230828_spring5?useSSL=false&useUnicode=true&characterEncoding=UTF8&serverTimezone=Asia/Shanghai
db.username=root
db.password=123456
2.5、AccountDO.java
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/24 8:22
* @Description:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
@ToString(callSuper = true)
public class AccountDO implements Serializable {
/**
* 主键
*/
private Integer id;
/**
* 银行卡号
*/
private String accountNo;
/**
* 账户余额
*/
private BigDecimal amount;
}
2.6、AccountMapper.java
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/24 8:23
* @Description:
*/
public interface AccountMapper {
/**
* 账户入账
* @param accountNo
* @param amount
* @return
*/
Integer accountEntry(@Param("accountNo") String accountNo, @Param("amount") int amount);
/**
* 账户支出
* @param accountNo
* @param amount
* @return
*/
Integer accountExpenditure(@Param("accountNo") String accountNo, @Param("amount") int amount);
}
2.7、AccountMapper.xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.star.mapper.AccountMapper">
<update id="accountEntry">
update account set amount = amount + #{amount} where account_no = #{accountNo}
</update>
<update id="accountExpenditure">
update account set amount = amount - #{amount} where account_no = #{accountNo}
</update>
</mapper>
2.8、AccountService.java
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/24 8:25
* @Description:
*/
public interface AccountService {
/**
* 转账
*/
void transferMoney();
}
2.9、AccountServiceImpl.java
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/24 8:25
* @Description:
*/
@Service
public class AccountServiceImpl implements AccountService {
@Resource
private AccountMapper accountMapper;
@Resource
private DataSourceTransactionManager transactionManager;
/**
* 转账
*
* @return
*/
@Override
public void transferMoney() {
TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
try {
// Jack 转出100元
accountMapper.accountExpenditure("Jack", 100);
// 模拟异常
int i = 10 /0;
// Rose 入账100元
accountMapper.accountEntry("Rose", 100);
// 提交事务
transactionManager.commit(transactionStatus);
} catch (Exception e) {
e.printStackTrace();
// 回滚事务
transactionManager.rollback(transactionStatus);
}
}
}
2.10、SpringJunitTest.java
/**
* @Author : 一叶浮萍归大海
* @Date: 2023/11/23 19:12
* @Description: Spring整合单元测试
*/
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class SpringJunitTest {
@Resource
private AccountMapper accountMapper;
@Resource
private AccountService accountService;
/**
* 入账 & 出账
*/
@Test
public void accountEntryTest() {
Integer entryResult = accountMapper.accountEntry("Jack", 100);
Integer roseResult = accountMapper.accountExpenditure("Rose", 100);
log.info("entryResult:{},roseResult:{}",entryResult,roseResult);
}
/**
* 转账
*/
@Test
public void transferMoneyTest() {
accountService.transferMoney();
}
}