测试环境:IDEA 2020.2、MySQL 8.0.16
项目结构:
pom.xml的内容(注意:spring-tx依赖aspectjweaver):
<?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>P039_TransferProblem_spring-tx_annotation-tx_xml</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-tx</artifactId> <!--注意:spring-tx依赖aspectjweaver--> <version>5.1.8.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.10</version> </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:tx="http://www.springframework.org/schema/tx" 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="cn.liuxingchang"></context:component-scan> <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.core.JdbcTemplate" id="jdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="txManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:advice id="tx" transaction-manager="txManager"> <tx:attributes> <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" timeout="-1" read-only="false"/> </tx:attributes> </tx:advice> <!--此处的transaction-manager属性用于配置全局事务管理器。 若省略transaction-manager属性,则采用默认值transaction-manager="transactionManager" @Transactional可临时指定并覆盖事务管理器--> <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven> </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.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.support.JdbcDaoSupport; import org.springframework.stereotype.Repository; import java.util.List; @Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private 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.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("accountService") @Transactional(readOnly = false, propagation = Propagation.REQUIRED) //@Transactional可作用于类和方法上,方法上的优先级高于类上的优先级 public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; public void transfer(String sourceName, String targetName, Double money) { /** * 1、根据sourceName查找souce账户 * 2、根据targetName查找target账户 * 3、souce账户减钱 * 4、target账户加钱 * 5、更新souce账户 * 6、更新target账户 */ //进行操作 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); } }
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); } }
说明1:
<tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>
上面的transaction-manager属性用于配置全局事务管理器。 若省略transaction-manager属性,则采用默认值transaction-manager="transactionManager" @Transactional可临时指定并覆盖事务管理器
说明2:
@Transactional可作用于类和方法上,方法上的优先级高于类上的优先级