看了慕课的spring事务管理,然后整理了一下,以便以后自己学习

时间:2022-06-06 10:50:59
事务的特性:
    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 的都删除)

1. 配置事务管理
   <!-- 创建事务管理 -->
	<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 然后进行测试会 报异常

看了慕课的spring事务管理,然后整理了一下,以便以后自己学习

因为进行的是修改操作.所有当设置为只读的话 就会报异常


上面那个是第一种方式 基于 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