1. 原子性.
事务是一个不可分割的整体,要么都失败,要么都成功
2. 一致性:
事务前后性必须数据的完整性必须保持一致
3. 隔离性:
多个用户并发访问数据库的时候,一个用户的事务不能被其他用户的事务给干扰,
多个并发事务之间数据要相互隔离
4. 持久性:
持久性就是事务一旦提交,那么对于数据库中就是永久性的,不会因为数据库发送事故而改变
事务管理的三个主要接口
platformTransactionManager
事务管理器
spring为不同的持久层框架提供了不同的接口
对于 mybatis,jdbc 使用的是 org.springframework.jdbc.datasource.DataSourceTransaction
对于 hibernate org.springframework.orm.hibernate3.HibernateTransactionManager
对于 jpa org.springframework.orm.jpa.JpaTransactionManager
对于 jdo org.springframework.orm.jdo.JdoTransactionManager
对于 jta(是一个分布式事务) org.springframework.orm.jta.JtaTransactionManager
TransactionDefinition
事务定义信息 (隔离,传播,超时,只读)
TransactionStatus
事务具体运行状态
事务中出现的问题:
1. 脏读:
一个事务读到了另一个事务改写但还未提交的数据,如果这些数据回滚,则读到的数据是无效的
2. 不可重复读:
在同一事务中,多次读取同一数据返回的结果有所不同
3. 幻读:
在一个事务中读取了几行数据,当另一个事务插入了几条数据后,在后来的查询后,发现一些没有的数据.
事务的隔离级别:
DEFAULT 使用后端数据库默认的隔离级别
对于mysql 默认的隔离级别(REPEATABLE_READ)
oracle (READ_COMMITED)
READ_UNCOMMITED 允许读取修改还未提交的数据,可能会导致,脏,不可重复,幻读的情况
READ_COMMITED 允许在事务提交以后读取,但不可避免不可重复,幻读的情况
REPEATABLE_READ 对于相同字段多次读取时一致的,除非数据被事务本身改变,可防止脏读,不可重复..但幻读不行
SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏,幻,不可重复读,这是所有隔离级别中最慢的,它是完全
锁在事务中涉及的数据表来完成的.
事务的传播行为:
当一个功能 需要调用 2个业务层的方法 才能完成, 每个service层的方法都有事务.\
PROPAGATION_REQUIRED * 支持当前事务,如果不存在就新建一个
PROPAGATION_SUPPORTS 支持当前事务,如果不存在就不适用事务,
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
PROPAGATION_REQUIRED_NEW * 如果有事务存在,挂起当前事务,新建一个
PROPAGATION_NOT_SUPPORTS 以非事务方式运行,如果事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,抛出异常
PROPAGATION_NESTED * 如果当前事务存在咋嵌套事务执行
spring 支持两种事务管理
1. 编程式的事务管理
1. 在实际应用中很少使用
2. 通过TransactionTemplate手动管理事务
使用XML配置声明式事务
1. 开发中推荐使用(代码侵入最小)
2. Spring的声明式事务式通过AOP实现的
1. 基于 TransactionProxyFactoryBean (很少使用)
2. 基于 AspectJ的XML方式 (经常使用)
一旦配置好了,不需要在添加任何东西
3. 基于 注解 (经常使用)
配置简单,需要在每个业务层类上面加 @Transcational 注解
做的一个实例 是 转账问题 a 转 b 两个人的账户相应增加,减少,当发生异常,则事务不进行提交,a,b的金额没有发生改变
在 SpringTest 测试类中 需要加入
@RunWith(SpringJUnit4ClassRunner.class) // //使用junit4进行测试
@ContextConfiguration("classpath:applicationContext.xml") // 加载配置文件
这两个注解
搭建测试环境
userDao,userDaoImpl
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class UserDaoImpl extends JdbcDaoSupport implements UserDao{
@Override
public void outPrice(String name, double price) {
// TODO Auto-generated method stub
String sql = "update user set price = price - ? where username = ?";
this.getJdbcTemplate().update(sql,price,name);
}
@Override
public void intPrice(String name, double price) {
// TODO Auto-generated method stub
String sql = "update user set price = price + ? where username = ?";
this.getJdbcTemplate().update(sql,price,name);
}
}
public interface UserDao {
/**
* 转账
*/
public void outPrice(String name,double price);
/**
* 收账
*/
public void intPrice(String name,double price);
}
userService,userServiceImpl
public interface UserService {
public void out(String name,String name2,double price);
}
import javax.annotation.Resource;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
public class UserServiceImpl implements UserService{
@Resource(name="userDao")
public UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
// 事务模板
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
}
@Override
public void out(String name, String name2, double price) {
// TODO Auto-generated method stub
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus tr) {
// TODO Auto-generated method stub
userDao.intPrice(name, price);
int a = 1 / 0;
userDao.outPrice(name2, price);
}
}
);
}
}
1. 编程式的事务管理
在 service 中使用TransactionTemplate
TransactionTemplate 依赖 DataSourceTransactionManager
DataSourceTransactionManager依赖DataSource构造
1.1 在 applicationContext 中配置事务管理器
<?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: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-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
>
<!-- 数据库配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 创建数据源 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" >
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${name}"></property>
<property name="password" value="${password}"></property>
<property name="maxActive" value="10"></property>
<property name="maxIdle" value="5"></property>
</bean>
<!-- 配置数据层 -->
<bean id="userDao" class="com.zyh.UserDaoImpl" scope="prototype">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置业务层 -->
<bean id="userService" class="com.zyh.UserServiceImpl" scope="prototype">
<property name="userDao" ref="userDao"></property>
<!-- 注入事务管理的模板 -->
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>
<!-- 编程式的事务管理 -->
<!-- 创建事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 将数据连接注入过来 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建一个 事务管理模板 简化 底层代码 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!-- 将真正进行事务管理的 注入进来 -->
<property name="transactionManager" ref="transactionManager"></property>
</bean>
</beans>
1.2 在 service 实现类中注入 transactionTemplate 模板来实现事务管理
// 当在service 方法中注入了transactionTemplate 那么在 applicationContext 中相应的 service
// 也注入transactionTemplate
private TransactionTemplate transactionTemplate;
public void setTransactionTemplate(TransactionTemplate transactionTemplate){
this.transactionTemplate = transactionTemplate;
}
然后调用 transactionTemplate 中的方法 execute
此方法呢需要一个匿名内部类 new TransactionCallbackWithoutResult(){
@Override
protected void doInTransactionWithoutResult(TransactionStatus tr) {
// TODO Auto-generated method stub
userDao.intPrice(name, price);
int a = 1 / 0;
userDao.outPrice(name2, price);
}
}
1.3 然后进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest {
@Resource(name="userService")
public UserService userService;
public void setUserService(UserService userService) {
this.userService = userService;
}
@Test
public void springDemo() {
userService.out("aaa", "bbb", 200);
}
}
然后查看数据库,发现中间发生异常,事务是不会提交的
2. spring声明式的事务管理
声明式的事务管理是基于spring aop思想的
将环境恢复到 一开始状态,(将所有的关于 transactionTemplate 的都删除)
<!-- 创建事务管理 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 将数据连接注入过来 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
2. 配置代理
<!-- 配置 代理 -->
<bean id="userServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置目标对象 -->
<property name="target" ref="userService"></property>
<!-- 注入事务管理 -->
<property name="transactionManager" ref="transactionManager"></property>
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<!--
prop格式:
PROPAGATION : 事务的传播行为
ISOLATION : 事务的隔离级别
readOnly : 只读
-Exception : 发生哪些异常回滚事务
+Exception : 发生那修异常事务不会滚
-->
<prop key="out">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
然后service层不需要再进行修改了,只需要修改SpringTest
将userService的 注入换为 userServiceProxy
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class SpringTest {
/**
* 注入代理类
*/
// @Resource(name="userService")
@Resource(name="userServiceProxy")
public UserService userService;
@Test
public void springDemo() {
userService.out("aaa", "bbb", 200);
}
}
然后进行测试.成功, 当有异常的时候是不会进行 事务提交,没有异常正常提交事务.
然后在 配置文件 bean userServiceProxy 的 事务属性中
<!-- 注入事务属性 -->
<property name="transactionAttributes">
<props>
<!--
prop格式:
PROPAGATION : 事务的传播行为
ISOLATION : 事务的隔离级别
readOnly : 只读
-Exception : 发生哪些异常回滚事务
+Exception : 发生那修异常事务不会滚
-->
<prop key="out">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
多配置一个readonly 然后进行测试会 报异常
因为进行的是修改操作.所有当设置为只读的话 就会报异常
上面那个是第一种方式 基于 TransactionProxyFactoryBean的原始方式
缺点 只能对一个业务层类 管理,当有多个模块的话就需要配置多个Proxy来实现事务管理
声明式事务管理,第二种方式As
在 applicationConext 中
<!-- 配置事务的通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--
propagation : 事务传播行为
isolation : 事务隔离级别
readOnly : 只读
rollback-for : 发生哪些异常回滚
no-rollback-for: 发生哪些异常不回滚
timeout : 过期信息
-->
<tx:method name="out" propagation="REQUIRED" isolation="DEFAULT" />
</tx:attributes>
</tx:advice>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut expression="execution(* zyh.UserService+.*(..))" id="pointcut1"/>
<aop:advisor advice-ref="pointcut1" pointcut="pointcut1"/>
</aop:config>
这种方式的只需要在配置文件配置后就可以 实现对事务管理, 代理对象在对应类生成时生成
第三种 基于注解的 声明式事务管理
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
然后只需要在 指定的类上面添加Transactional
/**
* propagation : 事务传播行为
isolation : 事务隔离级别
readOnly : 只读
rollback-for : 发生哪些异常回滚
no-rollback-for: 发生哪些异常不回滚
timeout : 过期信息
*/
@Transactional
public class UserServiceImpl implements UserService