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