实现简介:使用spring事务管理,联系两个用户之间转账问题,如果转账中途中出现异常,那就一起回滚不提交事务。
1创建数据库表
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`money` int(255) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'tom', 100);
INSERT INTO `user` VALUES (2, 'admin', 300);
SET FOREIGN_KEY_CHECKS = 1;
2.导入maven依赖
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- spring 依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!-- 德鲁伊连接池依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.20</version>
</dependency>
<!--aop 切面需要的依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>net.sourceforge.cglib</groupId>
<artifactId>com.springsource.net.sf.cglib</artifactId>
<version>2.2.0</version>
</dependency>
<dependency>
<groupId>org.aopalliance</groupId>
<artifactId>com.springsource.org.aopalliance</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
<version>1.6.4.RELEASE</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<!--spring对jdbc的封装-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--spring整合其他框架的使用-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--Spring5.x 日志封装依赖 -->
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-slf4j-impl -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.1</version>
<scope>test</scope>
</dependency>
<!-- spring5 测试依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.6.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<!-- 测试依赖-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
3.实体类
public class User {
private Integer id;
private String username;
private Integer money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
public User(Integer id, String username, Integer money) {
this.id = id;
this.username = username;
this.money = money;
}
public User() {
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", money=" + money +
'}';
}
}
4.1配置类
@Configuration //配置类
@ComponentScan(basePackages = "com.compass.hello") //开启组件扫描
@EnableTransactionManagement // 开启事务
public class SpringConfig {
// 配置数据连接
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUsername("root");
dataSource.setPassword("admin");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost/compass?serverTimezone=GMT%2B8");
return dataSource;
}
// 创建JdbcTemplate对象
@Bean
public JdbcTemplate setJdbcTemplate(DataSource dataSource){
JdbcTemplate template = new JdbcTemplate();
template.setDataSource(dataSource);
return template;
}
// 创建事务管理器
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
4.2配置文件
<?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:aop="http://www.springframework.org/schema/aop"
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 http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
">
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.compass.hello"> </context:component-scan>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="clone">
<property name="url" value="jdbc:mysql://localhost/compass?serverTimezone=GMT%2B8"> </property>
<property name="username" value="root"> </property>
<property name="password" value="admin"> </property>
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"> </property>
</bean>
<!-- 配置 JdbcTemplate 对象,注入 DataSource -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" >
<!-- 将数据库连接池的信息注入到 dataSource 对象中-->
<property name="dataSource" ref="dataSource"> </property>
</bean>
<!-- spring配置文件配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"> </property>
</bean>
<!--2 配置通知-->
<tx:advice id="txAdvice">
<!--配置事务参数-->
<tx:attributes>
<!--指定哪种规则的方法上面添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--<tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!--3 配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.compass.hello.service.impl.GetMoneyServiceImpl.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
5.1dao层接口
public interface GetMoneyDao {
/**
* 加钱的方法
*/
int addMoney(String username);
/**
*减钱的方法
*/
int reduceMoney(String username);
}
5.2dao层实现
import com.compass.hello.dao.GetMoneyDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class GetMoneyDaoImpl implements GetMoneyDao {
@Autowired
private JdbcTemplate jdbcTemplate;
//加钱方法
public int addMoney(String username) {
String sql = "update user set money=money+100 where username =?";
int updateCount = jdbcTemplate.update(sql,username);
return updateCount;
}
//减钱方法
public int reduceMoney(String username) {
String sql = "update user set money=money-100 where username=?";
int updateCount = jdbcTemplate.update(sql,username);
return updateCount;
}
}
6.service层实现类
@Service
public class GetMoneyServiceImpl {
@Autowired
private GetMoneyDao getMoney;
/**
* 同时去调用 加钱和减钱的方法
* @param sourceUsername 转账用户
* @param targetUsername 转账用户目标
*/
@Transactional() //开启事务管理
public void accountMoney(String sourceUsername,String targetUsername) {
// 调用到dao层加钱方法 (被转账用户)
getMoney.addMoney(targetUsername);
// int i = 1/0; //模拟网络异常
// 调用dao层减钱方法(转账用户)
getMoney.reduceMoney(sourceUsername);
}
}
7.测试
class ServiceTest {
//完全注解的方式
@Test
void accountMoney1(){
// 模拟用户转账,并且是在同一个方法中去完成
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
GetMoneyServiceImpl getMoneyService = context.getBean("getMoneyServiceImpl",GetMoneyServiceImpl.class);
getMoneyService.accountMoney("tom","admin");
}
//xml配置的方式
@Test
void accountMoney2(){
// 模拟用户转账,并且是在同一个方法中去完成
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("springConfig.xml");
GetMoneyServiceImpl getMoneyService = context.getBean("getMoneyServiceImpl",GetMoneyServiceImpl.class);
getMoneyService.accountMoney("tom","admin");
}
}