Spring 事务管理有两种方式:声明式事务和编程式事务。其中,编程式事务是基于TransactionTemplate的手动管理,不太常用,我们常用的是声明式事务。声明式事务又有三种实现方式:
- 基于TransactionProxyFactoryBean的方式
- 基于AspectJ的XML方式
- 基于注解的方式
一、基于注解的方式
(一)在Spring的xml文件中的配置
1. 配置事务管理器
2. 开启事务注解
在需要开启事务管理的方法上加上注解 @Transactional。
在 @Transactional 注解中可以进行如下属性设置:
-
propagation:指定事务的传播行为(即当前的事务方法被另外一个事务方法调用时,如何使用事务, 默认取值为 REQUIRED,
即使用调用方法的事务)。 -
isolation:指定事务的隔离级别,最常用的取值READ_COMMITTED。
-
默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对应的属性进行设置。通常情况下取默认值即可。
-
readOnly:指定事务是否为只读,表示这个事务只读取数据但不更新数据。这样可以帮助数据库引擎优化事务.。若真的是一个只读取数据库值的方法,应设置 readOnly=true。
-
timeout:指定强制回滚之前事务可以占用的时间。若超过了timeout指定的时间还为执行,则会抛出异常。
(二) 案例
银行转账业务。A用户向B用户转账,A用户减少一定的金额,B用户增加一定的金额,要么成功,要么失败。
1. 实体类Bean对象:
package com.zm.bean;
import org.springframework.stereotype.Component;
@Component("bank")
public class Bank {
private int id; //序号
private String cardId; //账号
private String userName; //用户名
private double money; //金额
public Bank() {
}
public Bank(int id,String cardId, String userName, double money) {
this.id = id;
this.cardId = cardId;
this.userName = userName;
this.money = money;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Bank{" +
"id=" + id +
", cardId='" + cardId + '\'' +
", userName='" + userName + '\'' +
", money=" + money +
'}';
}
}
2. dao层数据操作:
接口:
package com.zm.dao;
import com.zm.bean.Bank;
public interface BankDao {
//根据序号查询账户信息
public Bank findBankById(int id);
//更新bank表中金额信息 转走
public void updateFrom(int fromId,double money);
//转入
public void updateTo(int toId,double money);
}
实现类:
package com.zm.dao.impl;
import com.zm.autoException.BankException;
import com.zm.bean.Bank;
import com.zm.dao.BankDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository("bankDao")
public class BankDaoImpl implements BankDao {
@Autowired //自动装配
private JdbcTemplate template;
@Override
public Bank findBankById(int id) {
String sql = "select * from bank where id=?";
Bank bank = template.queryForObject(sql, new BeanPropertyRowMapper<Bank>(Bank.class), id);
return bank;
}
@Override
public void updateFrom(int fromId, double money) {
Bank bank = findBankById(fromId);
double yu = bank.getMoney();
if(yu<=0){
throw new BankException("已经没有余额了");
}else if(yu<money){
throw new BankException("余额不足,无法执行!");
}
String sql = "update bank set money = money - ? where id = ?";
int update = template.update(sql, money, fromId);
System.out.println(update);
}
@Override
public void updateTo(int toId, double money) {
String sql = "update bank set money = money + ? where id = ?";
int update = template.update(sql, money, toId);
System.out.println(update);
}
}
3. 业务逻辑(service)层:
业务接口:
package com.zm.service;
public interface TransferDao {
public void transferMoney(int fromId,int toId,double money);
}
实现类:
package com.zm.service.impl;
import com.zm.dao.BankDao;
import com.zm.service.TransferDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service("transferDao")
public class TransferImpl implements TransferDao {
@Autowired
private BankDao bankDao;
@Transactional //添加事务注解,表示使用事务管理机制
@Override //银行转账
public void transferMoney(int fromId,int toId,double money) {
/*A账户转向B账户*/
//从A账户中扣除相应的金额
bankDao.updateFrom(fromId,money);
//B账户中增加相应的金额
bankDao.updateTo(toId,money);
}
}
4. xml配置文件(applicationContext-tx.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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="com.zm"></context:component-scan>
<!--导入资源文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置druid数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<!--配置spring的JdbcTemplate-->
<bean id="template" class="org.springframework.jdbc.core.JdbcTemplate">
<!--引入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<!--使用哪个数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--启用事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
2、基于XML文件的配置方式
(一)xml文件中的配置方式
也可以在 <tx:method name="" /> 标签中设置事务的传播方式,隔离级别。是否可读等属性。
(二)案例:
银行转账业务。
1. 实体类对象:
public class Bank {
private int id; //序号
private String cardId; //账号
private String userName; //用户名
private double money; //金额
public Bank() {
}
public Bank(int id, String cardId, String userName, double money) {
this.id = id;
this.cardId = cardId;
this.userName = userName;
this.money = money;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCardId() {
return cardId;
}
public void setCardId(String cardId) {
this.cardId = cardId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Bank{" +
"id=" + id +
", cardId='" + cardId + '\'' +
", userName='" + userName + '\'' +
", money=" + money +
'}';
}
}
2. 数据操作的dao层:
接口:
package com.zm.xmlTx.dao;
import com.zm.xmlTx.bean.Bank;
public interface BankDao {
//根据序号查询账户信息
public Bank findBankById(int id);
//更新bank表中金额信息 转走
public void updateFrom(int fromId, double money);
//转入
public void updateTo(int toId, double money);
}
实现类:
package com.zm.xmlTx.dao.impl;
import com.zm.xmlTx.autoException.BankException;
import com.zm.xmlTx.bean.Bank;
import com.zm.xmlTx.dao.BankDao;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
public class BankDaoImpl implements BankDao {
private JdbcTemplate template;
public void setTemplate(JdbcTemplate template) {
this.template = template;
}
@Override
public Bank findBankById(int id) {
String sql = "select * from bank where id=?";
Bank bank = template.queryForObject(sql, new BeanPropertyRowMapper<Bank>(Bank.class), id);
return bank;
}
@Override
public void updateFrom(int fromId, double money) {
Bank bank = findBankById(fromId);
double yu = bank.getMoney();
if(yu<=0){
throw new BankException("已经没有余额了");
}else if(yu<money){
throw new BankException("余额不足,无法执行!");
}
String sql = "update bank set money = money - ? where id = ?";
int update = template.update(sql, money, fromId);
System.out.println(update);
}
@Override
public void updateTo(int toId, double money) {
String sql = "update bank set money = money + ? where id = ?";
int update = template.update(sql, money, toId);
System.out.println(update);
}
}
3. 业务逻辑层(service):
接口:
package com.zm.xmlTx.service;
public interface TransferDao {
public void transferMoney(int fromId, int toId, double money);
}
实现类:
package com.zm.xmlTx.service.impl;
import com.zm.xmlTx.dao.BankDao;
import com.zm.xmlTx.service.TransferDao;
public class TransferImpl implements TransferDao {
private BankDao bankDao;
public void setBankDao(BankDao bankDao) {
this.bankDao = bankDao;
}
@Override //银行转账
public void transferMoney(int fromId,int toId,double money) {
/*A账户转向B账户*/
//从A账户中扣除相应的金额
bankDao.updateFrom(fromId,money);
//B账户中增加相应的金额
bankDao.updateTo(toId,money);
}
}
4.xml文件(applicationContext-xml.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"
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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--导入资源文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!--配置druid数据源-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="initialSize" value="${jdbc.initialSize}"/>
<property name="minIdle" value="${jdbc.minIdle}"/>
<property name="maxActive" value="${jdbc.maxActive}"/>
</bean>
<!--配置spring的JdbcTemplate-->
<bean id="template" class="org.springframework.jdbc.core.JdbcTemplate">
<!--引入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--==================================================================================================-->
<!--将需要实例化的Bean对象注入到Spring Ioc的容器中-->
<bean id="bankDao" class="com.zm.xmlTx.dao.impl.BankDaoImpl">
<property name="template" ref="template"/>
</bean>
<bean id="transfer" class="com.zm.xmlTx.service.impl.TransferImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<!--==============================================================================================-->
<!--1. 配置事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<!--使用哪个数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!--2. 配置事务属性-->
<tx:advice transaction-manager="transactionManager" id="txAdvice">
<tx:attributes>
<!--
根据方法名指定事务的属性(传播方式,是否只可读,隔离级别)
propagation:设置事务的传播行为。有7中传播行为,默认值是REQUIRED
isolation:设置事务的隔离级别。
DEFAULT:可重复读(mysql) READ_UNCOMITTED:读未提交
READ_COMMITED:读已提交 REPEATABLE_READ SERLALIZABLE
read-only:是否只读。true只读 false不只读
timeout:设置等待的响应时间。若超过了这个时间还未执行,则抛出异常回滚。
* 所有方法,是一个通配符-->
<!-- <tx:method name="*"/>-->
<tx:method name="transferMoney" propagation="REQUIRES_NEW" />
</tx:attributes>
</tx:advice>
<!--3. 配置事务切入点,以及将事务切入点和事务属性关联起来-->
<aop:config>
<aop:pointcut expression="execution(* com.zm.xmlTx.service.impl.TransferImpl.*(..))"
id="txPointCut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
</beans>