Spring系列教程——15Spring事务管理器
上一篇:Spring系列教程——14Spring数据库操作讲解
下一篇:Spring系列教程——16Aop事务配置与注解事务配置
一.事务概念与特性讲解
1.概念
事务
是属于数据库操作
的概念,一个事务可有多个数据库操作组成,它是一个操作单元,组成它的操作作为一个整体,要么同时执行成功
,要么同时失败
。
2.特性(ACID)
1.原子性
即整体性,是指事务包含的所有操作要么全部成功,要么全部失败.
2.一致性
即数据一致,一个事务执行之前和执行之后都必须处于一致性状态,比如当在执行一个事务的时候,数据库系统发生了异常,这时事务执行了一般,导致只有执行了的那一部分操作才被写入数据库,而剩下的未被写入,这就是一个不一致的状态。
3.隔离性
也即并发性,一个事务的执行应该与其并发执行的其他事务互不干扰,互相隔离
4.持久性
也即永久性,一个事务一旦提交了,那么他对数据库的修改将是永久的
3.隔离级别
事务的隔离级别一共有四种,由低到高为Read uncommitted、Read committed、Repeatable read 、Serializable。但是会产生脏读
、不可重复复读
、虚读(幻读)
的问题。具体的例子可以参见这篇文章事务的四种隔离级别。
二.事务管理器介绍
1.准备讲解
首先我们需要讲解下面的红框里面的PlatformTransactionManager
,TransactionDefinition
,TransactionStatus
三个接口。
PlatformTransactionManager
:平台事务管理器,spring要管理事务,必须使用事务管理器,进行事务配置时,必须配置事务管理器。
TransactionDefinition
:事务详情(事务定义、事务属性),spring用于确定事务具体详情。例如:隔离级别、是否只读、超时时间等。进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
红框里面的内容我们理解,上面的内容介绍如下(关于传播行为
详细演示可以参见Spring事务传播行为详解,在后面讲事务代理工厂也会涉及到):
TransactionStatus
:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。spring底层根据状态进行相应操作。
接下来我们来看一个案例更好的理解.
1.准备表数据
:
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
money INT
);
INSERT INTO account(username,money) VALUES('jack','10000');
INSERT INTO account(username,money) VALUES('rose','10000');
2.dao层
:
IAccountDao接口
package dao;
@SuppressWarnings("all")
public interface IAccountDao {
//进账
public void inner(String inner,Integer money);
//扣钱
public void out(String outer,Integer money);
}
AccountDaoImpl实现类
package dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
@Override
public void inner(String inner, Integer money) {
getJdbcTemplate().update("update account set money = money + ? where username = ?",money,inner);
}
@Override
public void out(String outer, Integer money) {
getJdbcTemplate().update("update account set money = money - ? where username = ?",money,outer);
}
}
3.Service层
IAccountService接口
package service;
@SuppressWarnings("all")
public interface IAccountService {
public void transfer(String outter,String inner,Integer money);
}
AccountServiceImpl实现类
package service;
import dao.IAccountDao;
public class AccountServiceImpl implements IAccountService {
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
private IAccountDao accountDao;
@Override
public void transfer(String outter, String inner, Integer money) {
accountDao.inner(inner,money);
accountDao.out(outter,money);
}
}
4.配置文件
<?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"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="password" value="${password}"></property>
<property name="user" value="${user}"></property>
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
</bean>
<bean id="accountDao" class="dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
</beans>
5.测试
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
IAccountService service = (IAccountService) context.getBean("accountService");
service.transfer("jack","rose",1000);
接下来我们才正式开始事务管理的内容。
2.手动事务管理配置
这一部分我们将讲解Spring底层的事务管理的实现,但是我们对该部分只要了解就够了,因为后面都会通过Aop来实现。
spring底层使用
TransactionTemplate 事务模板
进行操作。
1.service 需要获得 TransactionTemplate
2.spring 配置模板,并注入给service
3.模板需要注入事务管理器
4.配置事务管理器:DataSourceTransactionManager ,需要注入DataSource
接下来我们按照上面的步骤来做。
1.在AccountServiceImpl里面配置模板
private TransactionTemplate template;
public void setTemplate(TransactionTemplate template) {
this.template = template;
}
@Override
public void transfer(String outter, String inner, Integer money) {
final String out = outter;
final String in = inner;
final Integer mon = money;
template.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
accountDao.inner(in,mon);
int i = 10/0;
accountDao.out(out,mon);
}
});
2.beans.xml修改为下面内容
<context:property-placeholder location="db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="password" value="${password}"></property>
<property name="user" value="${user}"></property>
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
</bean>
<!--配置事务管理器bean对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"></bean>
<!--配置事务模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!--配置事务管理器-->
<property name="transactionManager" ref="transactionManager"></property>
</bean>
<bean id="accountDao" class="dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<property name="template" ref="transactionTemplate"></property>
</bean>
3.测试
仍然使用上面的代码
在执行到int i=10/0;时出现异常,但是表数据由于回滚而未出现隔离性问题。
3.事务代理工厂配置
Spring提供管理事务的代理工厂bean :TransactionProxyFactoryBean
,这里我们采用的是AOP半自动
的方式来讲解使用方式。(前面讲过,忘记了可以参考Spring系列教程-12配置文件实现AOP)
这里我们不再需要TransactionTemplate
了,将tranfer
方法还原:
@Override
public void transfer(String outter, String inner, Integer money) {
accountDao.inner(inner,money);
int i = 10/0;
accountDao.out(outter,money);
}
beans.xml配置
的配置
<context:property-placeholder location="db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="password" value="${password}"></property>
<property name="user" value="${user}"></property>
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
</bean>
<bean id="accountDao" class="dao.AccountDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="service.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="proxyService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!--接口-->
<property name="proxyInterfaces" value="service.IAccountService"></property>
<!--目标类-->
<property name="target" ref="accountService"></property>
<!--切面类由Spring来完成-->
<!--配置事务管理器-->
<property name="transactionManager" ref="transactionManager"></property>
<!--事务属性、详情配置
prop.key :确定哪些方法使用当前事务配置
prop.text:用于配置事务详情
格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
传播行为 隔离级别 是否只读 异常回滚 异常提交-->
<property name="transactionAttributes">
<props>
</props>
</property>
</bean>
注意上面这一段:
<!--事务属性、详情配置
prop.key :确定哪些方法使用当前事务配置
prop.text:用于配置事务详情
格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
传播行为 隔离级别 是否只读 异常回滚 异常提交-->
<property name="transactionAttributes">
<props>
</props>
</property>
我们还没有对transactionAttributes
(事务属性)进行配置,我们先测试一下看可否保证数据安全。
这说明我们是事务管理工厂配置好后没有起到作用,原因在于我们没有配置transactionAttributes
来就行限定。配置这一段内容:
<property name="transactionAttributes">
<props>
<prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
</props>
</property>
接下来我们先将表数据修改回来。
测试发现此时发生异常后数据保持正常:
加入我们把配置改为:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
再来测试看看报什么异常:
还有-Exception,+Exception
没有演示,这里留给读者。(不过要注意不一定只可以写Exception,还可以类似这样+java.lang.ArithmeticException
)
上面的方式不是我们推荐的事务管理的配置方式,我们更推荐Aop配置与注解的方式,请见下一篇。
上一篇:Spring系列教程——14Spring数据库操作讲解
下一篇:Spring系列教程——16Aop事务配置与注解事务配置