• 07_Spring事务处理


    一.事务概述

    数据库的事务: 事务是一组操作的执行单元,相对于数据库操作来讲,事务管理的是一组SQL指令,比如增加,修改,删除等。事务的一致性,要求,这个事务内的操作必须全部执行成功,如果在此过程种出现了差错,比如有一条SQL语句没有执行成功,那么这一组操作都将全部回滚。

    事务的四大特性:

    atomic(原子性):要么都发生,要么都不发生。

    consistent(一致性):数据应该不被破坏。

    isolate(隔离性):用户间操作不相混淆

    durable(持久性):永久保存,例如保存到数据库中等

    编程式事务管理

    通过程序代码来控制你的事务何时开始,何时结束等,结果是控制的颗粒度更细,但需要手写程序,另外在团队合作开发时,会出现事务管理混乱。

    注意:如果使用Spring, 我们就不再需要手工控制事务

    声明式事务管理

    在Spring中,你只需要在Spring配置文件中做一些配置,即可将数据库的访问纳入到事务管理中,解除了和代码的耦合, 这是对应用代码影响最小的选择。当你不需要事务管理的时候,可以直接从Spring配置文件中移除该设置。

    Spring事务管理高层抽象主要包括3个接口

    1.PlatformTransactionManager(事务管理器)

    Spring为不同的持久化框架提供了不同PlatformTransactionManager接口实现,spring的事务管理器: spring没有直接管理事务,只是开发了事务管理器调用第三方组件完成事务控制。

    事务

    说明

    org.springframework.jdbc.datasource.DataSourceTransactionManager

    使用Spring JDBCiBatis 进行持久化数据时使用

    org.springframework.orm.hibernate3.HibernateTransactionManager

    使用Hibernate3.0版本进行持久化数据时使用

    org.springframework.orm.jpa.JpaTransactionManager

    使用JPA进行持久化时使用

    org.springframework.jdo.JdoTransactionManager

    当持久化机制是Jdo时使用

    org.springframework.transaction.jta.JtaTransactionManager

    使用一个JTA实现来管理事务,在一个事务跨越多个资源时必须使用

     

    2.TransactionDefinition(事务定义)

    ISOLation_XXX:事务隔离级别.

    PROPAGATION_XXX:事务的传播行为.

    3.TransactionStatus(事务具体运行状态)

    事务的传播行为

    Spring控制事务的方式:spring控制事务是以bean组件的函数为单位的,如果一个函数正常执行完毕,该函数内的全部数据库操作按照一次事务提交,如果抛出异常,全部回滚。

    事务的传播策略

    如两个bean组件都由spring控制事务,且组件的函数之间存在调用关系,即(bean1 函数a 调用了 bean2 函数b),spring提供了一组配置方式供开发者选择,这些配置方式称为事务的传播策略。

    数据库的隔壁级别

    脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。

    不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。

    幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。

    隔离级别

    含义

    DEFAULT

    使用后端数据库默认的隔离级别(spring中的的选择项)

    READ_UNCOMMITED

    允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读

    READ_COMMITTED

    允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生

    REPEATABLE_READ

    对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。

    SERIALIZABLE

    完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。

    一般使用,去头去尾

    只读事务与读写事务

    只读事务与读写事务:

      与所访问的数据库以及数据库驱动程序相关,并不一定是一个强制选项(例如在只读事务中去更新事务时允许的)。但若在事务中声明了只读事务,将会暗示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,不记录回滚日志等,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。

    使用场景:

    只读事务:单纯的数据库查询

    读写事务:对数据进行修改的操作。

    配置声明式事务控制

    1.在配置文件头信息上增加tx命名空间与tx.xsd:声明式事务控制需要引入tx标签,因此要引入此xsd文件。

    2.配置事务控制管理器:

    3.配置事务通知:配置哪些函数委托spring进行事务管理,以及事务管理的隔离级别、传播行为、是否只读事务属性。建议大家在需要更新数据的函数上配置隔离级别为数据库默认级别,传播行为采用required,读写事务。而只是查询的函数使用只读事务,效率更高

    4.配置事务的切入点:也就是spring的哪些组件要配置事务通知。

    在spring的三层架构中,建议把事务控制放在service层。

    实例:银行转账

    AccountDao.java
    public interface AccountDao {
        //存钱
        void addMoney(Integer id,Double money);
        //取钱
        void withDrawMoney(Integer id,Double money);
    }
    AccountDaoImpl.java
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
        @Override
        public void addMoney(Integer id, Double money) {
            String sql = "update Account set money = money + ? where id = ?";
            getJdbcTemplate().update(sql,money,id);
        }
    
        @Override
        public void withDrawMoney(Integer id, Double money) {
            String sql = "update Account set money = money - ? where id = ?";
            getJdbcTemplate().update(sql,money,id);
    
        }
    }
    AccountService.java
    public interface AccountService {
    
        //转账
        public void transfromMoney(Integer from_id, Double money,Integer to_id);
    }
    AccountServiceImpl.java
    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
    
        @Override
        public void transfromMoney(Integer from_id, Double money,Integer to_id) {
            //取钱
            accountDao.withDrawMoney(from_id,money);
            //存钱
            accountDao.addMoney(to_id,money);
        }
    
        public AccountDao getAccountDao() {
            return accountDao;
        }
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    }

    jdbc.properties

    jdbc.driverClass=com.jdbc.mysql.Driver
    jdbc.jdbcUrl=jdbc:mysql:///crm
    jdbc.user=root
    jdbc.password=123456

    applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns="http://www.springframework.org/schema/beans" 
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:aop="http://www.springframework.org/schema/aop" 
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd 
    	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd 
    	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd 
    	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd ">
    	
    	<!--1  读取属性文件方式<二> -->
    	<bean id="propertyConfigurer" 
    	class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    		<property name="location">
    			<value>classpath:jdbc.properties</value>
    		</property>
    	</bean> 
    	
    		
    	<!--2 c3p0数据源 (连接池)-->
    	<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    		<property name="driverClass" value="${jdbc.driverClass}"></property>
    		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    		<property name="user" value="${jdbc.user}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    	</bean>
    	<!-- 2.5 创建 jdbcTemplate 不需要了,因为我们继承了JdbcDaoSupport -->
    	<!-- <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    		<property name="dataSource" ref="dataSource"></property>
    	</bean> -->
    	<!--3  AccountDao对象 -->
    	<bean name="accountDao" class="com.icss.dao.AccountDaoImpl">
    		<property name="dataSource" ref="dataSource"></property>
    	</bean>
    	<!--4 AccountService对象  -->
    	<bean name="accountService" class="com.icss.service.AccountServiceImpl">
    		<property name="ad" ref="accountDao"></property>
    	</bean>
    	<!-- 声明式事务处理配置 -->
    	<!--1 创建核心事务管理,封装了所有事务的操作,事务也依赖于连接池 --> 
    	<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="dataSource"></property>
    	</bean>
    	<!--2 事务通知  -->
    	<tx:advice id="txAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
    			<!-- name:指方法的名称,*表示通配符 
    			    isolation:隔离级别
    				    1    读未提交 
    				    2    读己提交  oracle(默认)
    				    4    可重复读  mysql(默认)
    				    8     串行化
    			    propagation:传播行为(必须=REQUIRED  99.9999%)
    			    read-only:只读
    			-->
    			<tx:method name="add*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />
    			<tx:method name="save*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />	
    			<tx:method name="update*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />	
    			<tx:method name="modify*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />	
    			<tx:method name="delete*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />	
    			<tx:method name="remove*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />	
    			<tx:method name="get*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />	
    			<tx:method name="find*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />	
    			<tx:method name="query*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="true" />	
    			<tx:method name="trans*" isolation="REPEATABLE_READ" propagation="REQUIRED" read-only="false" />			
    		</tx:attributes>
    	</tx:advice>
    	<!--3织入将我们通知织入目标对象中  -->
    	<aop:config>
    		<!-- 切点 -->
    		<aop:pointcut expression="execution(* com.service.*ServiceImpl.*(..))" id="pc"/>
    		<!-- 切面   (通知+切点)-->
    		<aop:advisor advice-ref="txAdvice" pointcut-ref="pc"/>
    	</aop:config>
    </beans>
    
    
    
    
    

    结论:声明式事务配置步骤

    1.创建核心事物管理,分装了所有事物操作,事物也依赖连接池
    2.配置事物通知
    3:织入:将通知织入到目标对象中
    
  • 相关阅读:
    Qt QPainter::end: Painter ended whith 2 saced states
    2月6日学习内容
    2月5日学习总结
    2月4日所学内容
    2月3日学习内容
    2月2日学习收获
    2月1日学习内容
    构建之法读后感(一)
    11月从小工到专家读后感(二)
    11月从小工到专家的读后感(一)
  • 原文地址:https://www.cnblogs.com/jatpeo/p/11767526.html
Copyright © 2020-2023  润新知