一、Spring 事务
Spring提供对事务支持的核心包是spring-tx-4.3.6.RELEASE包。
该包类有三个核心接口,提供对事务的支持:
1.1PlatformTransactionManager
该接口是Spring提供的平台管理器,主要用于事务管理。
该接口提供了如下方法:
TranscationStatus getTransaction(TransactionDefinition definition);用于获取事务状态信息。
void commit(TransactionStatus status):提交事务
void rollback(TransactionStatus status):事务回滚
此接口的具体实现类:
org.springframework.jdbc.datasource.DataSourceTransactionManager(用于配置JDBC)。
1.2TransactionDefinition
TransactionDefinition是事务定义的对象,包含定义事务规则,获取事务信息等操作。
String getName();获取事务对象名称。
int getLsolationLeve();获取事务隔离级别。
int getPropagationBehavior();获取事务传播行为。
int getTimeout();获取事务超时时间。
boolean isReadOnly()判断事务是否只读。
1.3TransactionStatus
TransactionStatus是事务的状态,包含方法:
void flush();刷新事务
boolean isCompleted();判断事务是否完成。
boolean isNewTranscation();判断是否为新事务。
boolean isRollbackOnly();判断事务是否回滚。
void setRollbackOnly:设置事务回滚。
二、声明式事务管理
声明式事务管理式通过AOP将事务管理作为一个切面,然后将切面(事务管理)织入目标类中。
先看一个失败案例,未配置事务,进行数据库操作失败情况:
Account.java
public class Account { private Integer id; private String username; private Double balance; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Double getBalance() { return balance; } public void setBalance(Double balance) { this.balance = balance; } @Override public String toString() { return "Account [id=" + id + ", username=" + username + ", balance=" + balance + "]"; } }
AccountDao.java(定义数据库相关操作)
import java.util.List; public interface AccountDao { public int addAccount(Account account); public int updateAccount(Account account); public int deleteAccount(int id); public Account findAccountById(int id); public List<Account> findAllAccounts(); public void transfer(String outUser, String inUser, Double money);//转账 }
AccountDaoImpl.java (AccountDao的实现类)
import java.util.List; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; public class AccountDaoImpl implements AccountDao{ private JdbcTemplate jt; public void setJt(JdbcTemplate jt) { this.jt = jt; } @Override //添加用户 public int addAccount(Account account) { String sql = "insert into account(username,balance) value(?,?)"; Object[] obj = new Object[] { account.getUsername(), account.getBalance() }; int num = jt.update(sql,obj); return num; } @Override //更新用户 public int updateAccount(Account account) { String sql = "update account set username=?,balance=?,where id = ?"; Object[] obj = new Object[] { account.getUsername(), account.getBalance(), account.getId() }; int num = jt.update(sql,obj); return num; } @Override //删除用户 public int deleteAccount(int id) { String sql = "delete from account where id = ?"; int num = jt.update(sql,id); return num; } @Override //按ID查找 public Account findAccountById(int id) { String sql = "select * from account where id = ?"; //设置映射,即表中一行记录对应一个Accout对象(数据库中列名要和对象属性名一致) RowMapper<Account> rp = new BeanPropertyRowMapper<Account>(Account.class); return this.jt.queryForObject(sql, rp, id); } @Override//查找所有对象 public List<Account> findAllAccounts() { String sql = "select * from account"; RowMapper<Account> rp = new BeanPropertyRowMapper<Account>(Account.class); return this.jt.query(sql,rp); } @Override //转账 public void transfer(String outUser, String inUser, Double money) { //转账 this.jt.update("update account set balance = balance - ?" + "where username = ?", money, outUser); int i = 1/0; //模拟意外情况。 //收账 this.jt.update("update account set balance = balance + ?" + "where username = ?",money,inUser); } }
beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" > <bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name = "driverClassName" value = "com.mysql.jdbc.Driver" /> <property name = "url" value = "jdbc:mysql://localhost:3306/spring" /> <property name = "username" value = "root" /> <property name = "password" value = "123456" /> </bean> <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate"> <property name = "dataSource" ref = "dataSource" /> </bean> <bean id = "accountDao" class = "com.spring.db.AccountDaoImpl"> <property name="jt" ref = "jdbcTemplate"></property> </bean> </beans>
测试:
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; public class JDBCTemplateTest { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); AccountDao accountDao = (AccountDao)ac.getBean("accountDao"); Account hcf = new Account(); Account zrx = new Account(); //设置两个用户 hcf.setUsername("hcf"); hcf.setBalance(1000.0); zrx.setUsername("zrx"); zrx.setBalance(1000.0); //将用户添加到数据库 accountDao.addAccount(hcf); accountDao.addAccount(zrx); //zrx向hcf转账500 accountDao.transfer("zrx", "hcf", 500.0); System.out.println("成功"); } }
转账方法中人为添加一个错误,诸侯出现了转账的金额扣除了,但是收账方没有收到金额。
这种情况显然是我们不允许的。
这时就可以通过事务来实现,把转账功能作为一个事务。
即使中途出现错误没有将钱转给收账方,会发生回滚转账方的钱也不会扣除。
2.1基于XML的声明式事务
首先我们需要如下jar包:
先了解下xml中配置事务的元素:
<tx:advice>配置事务通知,
属性transaction-manager:指定事务管理器。
<tx:attributes>:<tx:advice>的子元素,用于配置事务细节。
<tx:method>:<tx:attributes>的子元素,用于配置会务相关信息。
属性:name:指定与事务关联的方法,可采用通配符*。
propagation:指定事务传播行为。
isolation;指定事务隔离级别,默认为REFAULT。
read-only:指定事务是否只读。
我们只需在benas.xml中配置事务相关信息,然后通过切面的配置,将目标方法织入即可。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" > <bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name = "driverClassName" value = "com.mysql.jdbc.Driver" /> <property name = "url" value = "jdbc:mysql://localhost:3306/spring" /> <property name = "username" value = "root" /> <property name = "password" value = "123456" /> </bean> <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate"> <property name = "dataSource" ref = "dataSource" /> </bean> <bean id = "accountDao" class = "com.spring.db.AccountDaoImpl"> <property name="jt" ref = "jdbcTemplate"></property> </bean> <!-- 事务管理 --> <!-- 配置事务管理,并制定数据源 --> <bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name = "dataSource" ref = "dataSource" /> </bean> <!-- 配置事务切面,将事务作为一个切面织入方法 --> <tx:advice id = "txAdvice" transaction-manager = "transactionManager"> <tx:attributes> <!-- *代表任意方法 传播行为 隔离级别 是否只读 --> <tx:method name = "*" propagation = "REQUIRED" isolation = "DEFAULT" read-only ="false"/> </tx:attributes> </tx:advice> <!-- 配置切面,制定切面,切入点 --> <aop:config> <aop:pointcut expression = "execution(* com.spring.db.*.*(..))" id = "txPointCut"/> <aop:advisor advice-ref = "txAdvice" pointcut-ref = "txPointCut"/> </aop:config> </beans>
在xml中添加事务配置,我们再来进行转账操作。
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.jdbc.core.JdbcTemplate; public class JDBCTemplateTest { public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml"); AccountDao accountDao = (AccountDao)ac.getBean("accountDao"); Account hcf = new Account(); Account zrx = new Account(); //zrx向hcf转账500 accountDao.transfer("zrx", "hcf", 500.0); System.out.println("成功"); } }
再次运行出现错误后数据库中的balance并没有被扣除。
转账和收账作为一个整体,一荣俱荣,一损俱损。
2.2基于Annotation的声明式事务
1)在xml中配置事务注解驱动
<tx:annotation-driven transaction-manager="transactionManager"/>
2)在需要使用事务的类(对类中所有方法有效)或方法(只对方法有效)上添加@Transactional注解。
@Transactional注解属性:
transactionManager:指定事务管理器。
isolation:指定事务隔离级。
noRollbackFor:用于指定遇到异常时强制不执行回滚。
noRollbackForClassName:指定遇到多个异常时强制不执行回滚。
该属性可指定多个异常类名。
propagation:指定事务传播行为。
read-only:设置事务是否只读,默认为false。
rollbackFor:用于指定遇到特定异常时强制发生回滚。
rollbackForClassName:指定遇到多个异常时强制执行回滚。
timeout:指定事务超时时长。
beans.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd" > <bean id = "dataSource" class = "org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name = "driverClassName" value = "com.mysql.jdbc.Driver" /> <property name = "url" value = "jdbc:mysql://localhost:3306/spring" /> <property name = "username" value = "root" /> <property name = "password" value = "123456" /> </bean> <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate"> <property name = "dataSource" ref = "dataSource" /> </bean> <bean id = "accountDao" class = "com.spring.db.AccountDaoImpl"> <property name="jt" ref = "jdbcTemplate"></property> </bean> <!-- 事务管理 --> <!-- 配置事务管理,并制定数据源 --> <bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name = "dataSource" ref = "dataSource" /> </bean> <!-- 配置事务驱动 --> <tx:annotation-driven transaction-manager = "transactionManager" /> </beans>
将转账方法加上注解:
@Override //转账 @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false) public void transfer(String outUser, String inUser, Double money) { //转账 this.jt.update("update account set balance = balance - ?" + "where username = ?", money, outUser); int i = 1/0; //模拟意外情况。 //收账 this.jt.update("update account set balance = balance + ?" + "where username = ?",money,inUser); }
原文:https://www.cnblogs.com/huang-changfan/p/10446932.html