测试环境:IDEA 2020.2、MySQL 8.0.16
项目结构:
pom.xml的内容:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>P041_programmatic_transaction_control</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
</dependencies>
</project>
bean.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: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/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--ComboPooledDataSource只能通过set方法(property标签)注入连接相关的信息-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" scope="singleton">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?characterEncoding=UTF8&serverTimezone=Asia/Shanghai"></property>
<property name="user" value="root"></property>
<property name="password" value="123456"></property>
</bean>
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean class="cn.liuxingchang.Dao.impl.AccountDaoImpl" id="accountDao">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="accountService" class="cn.liuxingchang.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
</beans>
MySQL数据库:
database:test port:3306 table:account
Account.java的内容:
package cn.liuxingchang.Domain;
import java.io.Serializable;
public class Account implements Serializable {
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
AccountDao.java的内容:
package cn.liuxingchang.Dao;
import cn.liuxingchang.Domain.Account;
public interface AccountDao {
void updateAccount(Account account);
Account findAccountByName(String name);
}
AccountDaoImpl.java的内容:
package cn.liuxingchang.Dao.impl;
import cn.liuxingchang.Dao.AccountDao;
import cn.liuxingchang.Domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void updateAccount(Account account) {
try {
jdbcTemplate.update("update account set name = ?, money = ? where id = ?",
account.getName(), account.getMoney(), account.getId());
} catch(Exception e) {
throw new RuntimeException(e);
}
}
public Account findAccountByName(String name) {
try {
List<Account> accounts = jdbcTemplate.query("select * from account where name = ?",
new BeanPropertyRowMapper<Account>(Account.class), name);
if(accounts == null || accounts.size() == 0) {
throw new RuntimeException("找不到目标账户!");
} else if(accounts.size() == 1) {
return accounts.get(0);
} else {
throw new RuntimeException("账户不唯一!");
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
AccountService.java的内容:
package cn.liuxingchang.service;
public interface AccountService {
void transfer(String sourceName, String targetName, Double money);
}
AccountServiceImpl.java的内容:
package cn.liuxingchang.service.impl;
import cn.liuxingchang.Dao.AccountDao;
import cn.liuxingchang.Domain.Account;
import cn.liuxingchang.service.AccountService;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
public void transfer(final String sourceName, final String targetName, final Double money) {
transactionTemplate.execute(new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus transactionStatus) {
Account source = accountDao.findAccountByName(sourceName);
Account target = accountDao.findAccountByName(targetName);
source.setMoney(source.getMoney() - money);
target.setMoney(target.getMoney() + money);
accountDao.updateAccount(source);
int i = 1 / 0; //用来测试事务的一致性
accountDao.updateAccount(target);
return null;
}
});
}
}
Test.java的内容:
import cn.liuxingchang.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
AccountService accountService = context.getBean("accountService", AccountService.class);
@org.junit.Test
public void transferTest() {
accountService.transfer("Zhao", "Qian", 1.0);
}
}