了解了事务特性之后,可以试一下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依赖