Spring编程式事务抽象

了解了事务特性之后,可以试一下Spring的事务操作

编程式事务使用TransactionTemplate或者直接使用底层PlatformTransactionManager来完成

事务管理的接口如下:

public interface PlatformTransactionManager extends TransactionManager {

TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;

}

一般还是直接用TransactionTemplate来进行操作

1、如果是用jdbc操作数据,那么事务管理就用DataSourceTransactionManager,实现了PlatformTransactionManager接口,确认了平台

2、需要知道DataSource,数据库的一些信息,这样才能确认操作的数据库

3、使用TransactionTemplate可以传入DataSourceTransactionManager,这样可以得到一个TransactionTemplate

4、最终可以用TransactionTemplate的execute方法进行操作

public <T> T execute(TransactionCallback<T> action) throws TransactionException {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
}
else {
TransactionStatus status = this.transactionManager.getTransaction(this);
T result;
try {
result = action.doInTransaction(status);
}

可以定义一个类实现TransactionCallback接口,作为execute参数,具体要做的操作放在doInTransaction里

 

测试一个简单的小例子

首先还是之前的这张表

+----+-------+-------------+-----+
| id | name  | phone       | age |
+----+-------+-------------+-----+
| 1  | lihui | 17777777777 | 18  |
| 2  | lilei | 18888888888 | 19  |
| 3  | litao | 19999999999 | 20  |
| 4  | lucy  | 16666666666 | 16  |
+----+-------+-------------+-----+

映射到一个类定义

package com.lihuia.jdbc;

import lombok.Builder;
import lombok.Data;

/**
* Copyright (C), 2018-2019
* FileName: User
* Author: lihui
* Date: 2019/10/31
*/

@Data
@Builder
public class User {

private String name;
private String phone;
private Integer age;
}

这里我想完成的是通过启动一个事务transaction,然后简单进行一下数据库操作,比如写一条数据,然后进行回滚,可以发现这条事务里数据回到了原始状态

因此首先这里要做数据库操作,配置一下dataSource数据源,我用的mysql,什么用户名,密码,驱动,url少不了

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
</bean>

配置信息如下

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/user_center
jdbc.username=root
jdbc.password=

通过在XML文件里指定查找位置

<context:property-placeholder location="classpath:config/db.properties"/>

接着就是配置Spring jdbcTemplate,最关键的是注入dataSource,ref一定不要丢失

<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>

这样,数据库CURD操作的jdbcTemplate已经配置好了

Transaction的相关操作同理这里用的是TransactionTemplate

<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>

 

整个XML文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">

<context:property-placeholder location="classpath:config/db.properties"/>
<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean name="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>



<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
</bean>

<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>

</beans>

 

完成了上面jdbcTemplate和transactionTemplate的依赖注入,测试类里就可以直接写整个测试流程

package com.lihuia.jdbc;

import java.util.List;
import java.util.Map;

import org.testng.annotations.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

/**
* Copyright (C), 2018-2019
* FileName: TransactionDemoTest
* Author: lihui
* Date: 2019/10/31
*/


@ContextConfiguration(locations = {
"classpath*:config/application.xml"
})
public class TransactionDemoTest extends AbstractTestNGSpringContextTests {

@Autowired
private TransactionTemplate transactionTemplate;

@Autowired
private JdbcTemplate jdbcTemplate;

private Integer getCount() {
String sql = "select count(*) from user_info";
return jdbcTemplate.queryForObject(sql, Integer.class);
}

private List<Map<String, Object>> getTable() {
String sql = "select * from user_info";
return jdbcTemplate.queryForList(sql);
}

private void insert(User user) {
String sql = "insert into user_info (name, phone, age) values (?, ?, ?)";
jdbcTemplate.update(sql, user.getName(), user.getPhone(), user.getAge());
}

@Test(description = "启动事务以及回滚")
public void transactionTest() {
System.out.println("Before Transaction Rows: " + getCount());
System.out.println("Table: " + getTable());
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
insert(User.builder()
.name("Jim")
.phone("15888888888")
.age(18)
.build());
System.out.println("After Transaction Insert Rows: " + getCount());
System.out.println("Table: " + getTable());
transactionStatus.setRollbackOnly();

}
});
System.out.println("After Transaction Roolback Rows: " + getCount());
System.out.println("Table: " + getTable());
}
}

将数据库的查询和插入封装一个方法,测试方法首先在启动事务之前,数据库有4条记录;启动事务执行transactionTemplate的execute方法,因为这里插入了一条数据,是没有返回值的,因此调用的是TransactionCallbackWithoutResult里面的执行方法,

然后在具体doInTransactionWithoutResult方法里执行插入操作,然后调用TransactionStatus的setRollbackOnly()方法强行回滚,这样刚插入的数据就会被回滚

执行的结果:

Before Transaction Rows: 4
Table: [{id=1, name=lihui, phone=17777777777, age=18}, {id=2, name=lilei, phone=18888888888, age=19}, {id=3, name=litao, phone=19999999999, age=20}, {id=4, name=lucy, phone=16666666666, age=16}]
After Transaction Insert Rows: 5
Table: [{id=1, name=lihui, phone=17777777777, age=18}, {id=2, name=lilei, phone=18888888888, age=19}, {id=3, name=litao, phone=19999999999, age=20}, {id=4, name=lucy, phone=16666666666, age=16}, {id=10, name=Jim, phone=15888888888, age=18}]
After Transaction Roolback Rows: 4
Table: [{id=1, name=lihui, phone=17777777777, age=18}, {id=2, name=lilei, phone=18888888888, age=19}, {id=3, name=litao, phone=19999999999, age=20}, {id=4, name=lucy, phone=16666666666, age=16}]

这样就满足了事务的回滚

这里pom里的依赖如下:

<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
</dependencies>

这里我用的是<bean id=”dataSource” class=”org.springframework.jdbc.datasource.DriverManagerDataSource”>,因此需要添加mysql的依赖,网上用<bean id=”dataSource” class=”org.apache.commons.dbcp2.BasicDataSource”>,试了下这个是不需要mysql依赖

发表评论