概述
事务是一组逻辑上的操作,这组操作,要么全部成功,要么全部失败。不会存在一部分操作失败,一部分操作成功的情形。
事务特性
事务的四个属性:原子性、一致性、隔离性、持久性。
spring对事务的管理,主要的API在spring-tx-version.jar中,spring提供了三个主要的接口:
spring为不同的持久化框架,提供了不同PlatformTransactionManager接口实现。
隔离性是为了解决脏读,不可重复读,幻影读问题的。
隔离级别分为四种(DEFAULT是衍生出来的):
传播行为主要是用来解决多个业务之间方法相互调用时事务管理的问题。
事务的传播行为分为以下几种行为:
编程式事务管理是比较贴近jdbc时代,手动控制事务,例如:conn.setAutoCommit(false),conn.rollback()等。这种方式在开发中已经很少使用了,至于原因,看过这个示例,你就能明白。需要事务的地方,需要手动编写事务代码,虽然有可用的模板,使用方式也固定,但是随着业务方法的增加,这种频繁编写事务的代码,实在是不敢想象。
示例
这里使用maven工程,工程结构如下:
pom.xml
junit
junit
4.12
test
org.aspectj
aspectjweaver
1.8.13
org.springframework
spring-context-support
4.3.4.RELEASE
org.springframework
spring-tx
4.3.4.RELEASE
org.springframework
spring-jdbc
4.3.4.RELEASE
org.springframework
spring-test
4.3.4.RELEASE
mysql
mysql-connector-java
5.1.38
jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///shop?useUnicode=true&useSSL=false
jdbc.username=hadoop
jdbc.password=hadoop
spring.xml
AccountDao.java
package com.xxx.springtransaction.dao;
public interface AccountDao {
public void transferIn(String id,Double money);
public void transferOut(String id,Double money);
public Double findById(String id);
}
AccountDaoService.java
package com.xxx.springtransaction.dao.impl;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import com.xxx.springtransaction.dao.AccountDao;
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
@Override
public void transferIn(String id, Double money) {
String sql = "update account set money = money + ? where id = ?";
getJdbcTemplate().update(sql, money,id);
}
@Override
public void transferOut(String id, Double money) {
String sql = "update account set money = money - ? where id = ?";
getJdbcTemplate().update(sql, money,id);
}
@Override
public Double findById(String id){
return null;
}
}
AccountService.java
package com.xxx.springtransaction.service;
public interface AccountService {
public void transfer(String out,String in,Double money);
public Double findById(String id);
}
AccountServiceImpl.java
package com.xxx.springtransaction.service.impl;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.xxx.springtransaction.dao.AccountDao;
import com.xxx.springtransaction.service.AccountService;
public class AccountServiceImpl implements AccountService {
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void transfer(final String out, final String in, final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus
transactionStatus) {
accountDao.transferOut(out, money);
//int i = 1/0;
//System.out.println(i);
accountDao.transferIn(in, money);
}
});
}
@Override
public Double findById(String id){
return accountDao.findById(id);
}
}
单元测试类:SpringTransactionTest.java
package com.xxx.springtransaction;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.xxx.springtransaction.service.AccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring.xml")
public class SpringTransactionTest {
@Resource
private AccountService accountService;
@Test
public void demo1(){
accountService.transfer("1", "2", 200d);
}
}
准备数据,这里假设有一张账户表account,里面有三个账户,分别是aaa,bbb,ccc,均有1000元,这里模拟aaa给bbb转账200元。
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | aaa | 1000 |
| 2 | bbb | 1000 |
| 3 | ccc | 1000 |
+----+------+-------+
3 rows in set
测试验证方法:
1、在没有事务管理或者正常的情况下,aaa给bbb直接转账,会成功,aaa的账户变为800,bbb的账户变为1200
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | aaa | 800 |
| 2 | bbb | 1200 |
| 3 | ccc | 1000 |
+----+------+-------+
3 rows in set
2、在没有事务管理的情况下,打开AccountServiceImpl.java中以下代码注释,让业务方法抛出异常。
int i = 1/0;
System.out.println(i);
这时候转账,aaa的账户会转出200,但是bbb的账户因为异常,不会增加200.
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | aaa | 600 |
| 2 | bbb | 1200 |
| 3 | ccc | 1000 |
+----+------+-------+
3 rows in set
3、给业务代码加上事务管理代码,如下所示,再次转账,抛出异常,事务回滚,aaa的账户钱数不会减少,bbb的不会增加:
@Override
public void transfer(final String out, final String in, final Double money) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus
transactionStatus) {
accountDao.transferOut(out, money);
int i = 1/0;
System.out.println(i);
accountDao.transferIn(in, money);
}
});
}
转账结果:
mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
| 1 | aaa | 600 |
| 2 | bbb | 1200 |
| 3 | ccc | 1000 |
+----+------+-------+
3 rows in set
这个结果说明事务管理生效了。