zoukankan      html  css  js  c++  java
  • Spring六、Spring ORM

    一、数据库事务

    1. 事务的基本概念

      事务(Transaction)是访问并可能更新数据库中各项数据项的一个执行单元(unit)。是恢复和并发控制的基本单位。
      事物的ACID4个特性:原子性、一致性、隔离性、持久性

      原子性(Automicity):一个事务是一个不可分割的工作单位,事务中的事情要么都做、要么都不做。
      一致性(Consistency):事务必须是使数据库从一个一致性状态到另一个一致性状态,一致性与原子性是密切相关的。
      隔离性(Isolation):一个事务的执行不能被其它事务所干扰。即一个事物内所修改及使用得数据对其它并行的事务来说是隔离的,并发执行的各个单位不能互相干扰。
      持久性(Durability):持久性也成永久性,指一个事物一旦提交,它对数据库的改变应该是永久性的。 
    
    

    2. 数据库隔离级别

      读未提交、读已提交、可重复读、串行化。

    隔离级别 隔离级别的值 导致的问题
    Read-Uncommited 0 导致脏读
    Read-Committed 1 避免脏读;允许不可重复读、幻读;
    Repeatable-Read 2 避免脏读、不可重复读;允许幻读;- 类似 行锁
    Serializable 3 串行化,一个事务执行完才能执行下一个,避免脏读、不可重复读、幻读; 执行效率慢,使用时慎重;- 类似表锁

      脏读:事务可以读取别的事务还未提交的数据。比如同一条数据,A事务先读取到然后做了修改但事务并未提交,此时B事务去读取数据会读取到A事务修改后的数据,此时A事务回滚,B事务也就读取到了脏数据。
      不可重复读:针对单条数据。一个事物中发生了两次读操作,在第一次读操作和第二次读操作中,另一个事务对数据做了修改并提交了事务,则在第一个事务内两次读取到的数据时不同的。
      幻读:针对多条数据。A事务执行批量更新操作,更新了一批数据;B事务在这个范围内新增了一条数据,此时A事务并不会对这条数据做修改。
      数据库隔离级别设置的越高,越能保证数据的完整性和一致性,但是对并发的性能影响也越大。
      大部分数据库的隔离级别默认是读已提交(Read-Commited),如SqlServer、Oracle。
      少数数据库的默认隔离级别是可重复读(Repeatable-Read),如Mysql、InnoDB

    二、Spirng事务

    1. Spring事物的基础配置

      先来看下常用的Spring事物的基础配置

    	<aop:aspectj-autoproxy proxy-target-class="true"/>
    	
    	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="dataSource"/>
    	</bean>
    
    	<!-- 配置事务通知属性 -->
    	<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
    			<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/>
    			<tx:method name="remove*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/>
    			<tx:method name="edit*" propagation="REQUIRED" rollback-for="Exception,RuntimeException,SQLException"/>
    			<tx:method name="login" propagation="NOT_SUPPORTED"/>
    			<tx:method name="query*" read-only="true"/>
    		</tx:attributes>
    	</tx:advice>
    
    	<aop:config>
    	  <aop:advisor advice-ref="transactionAdvice" pointcut-ref="transactionPointcut"/>
              <aop:aspect ref="dataSource">
                <aop:pointcut id="transactionPointcut" expression="execution(public * com.gupaoedu..*.service..*Service.*(..))" />
              </aop:aspect>
            </aop:config>
    	   
    

      Spring事物的管理基于Spring AOP实现,同一封装非功能性需求。

    2. Spring事物的基本原理

      Spring事物的本质其实是数据库对事务的支持,没有数据库的事务支持,Spring是无法提供事务功能的。对于底层使用JDBC操作数据库,用到事务的逻辑,可以参考下面的demo:

        public void testJdbc() {
            Connection conn = null;
            Statement stmt = null;
    
            try {
                // 注册 JDBC 驱动
                // Class.forName("com.mysql.jdbc.Driver");
    
                // 1. 打开连接
                conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis", "root", "");
    
                // 2. 开启手动提交事务
                conn.setAutoCommit(false);
    
                // 3. 执行CURD
                stmt = conn.createStatement();
                String sql = "update blog set name='测试' where bid = 1";
                int i = stmt.executeUpdate(sql);
    
                // 4.提交事务
                conn.commit();
                System.out.println(i);
    
            } catch (Exception e) {
                try {
                    // 4.回滚事务
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
                e.printStackTrace();
            } finally {
                try {
                    // 5.释放资源
                    if (stmt != null) stmt.close();
                } catch (SQLException se2) {
                }
                try {
                    if (conn != null) conn.close();
                } catch (SQLException se) {
                    se.printStackTrace();
                }
            }
        }
    

      Spring项目日常开发我们经常使用@Transactional注解来来进行事物管理,而不再需要我们写步骤2和步骤4,那么Spring又是怎么实现的呢?
      首先在程序启动时,Spring会根据我们配置的component-scan扫描包路径,解析相关的bean,这时候会查看这个类以及方法上的注解,针对使用了@Transactional的类和方法会重新生成代理,并根据@Transactional的相关参数进行配置注入,在代理类中字节码重组重新为原逻辑添加了开启事务,正常执行提交事务、异常回滚事务的逻辑。真正的数据库事务的提交和回滚则是由其内部机制,通过binlog或者redo log实现的。

    3. Spring事务的传播属性

      在使用@Transactional注解时,默认传播属性为Propagation propagation() default Propagation.REQUIRED;

    常量名称 解释
    Propagation.REQUIRED 支持当前事务,如果当前没有事务就新建一个事务。这种是Spring最常见的传播属性,也是Spring默认的事务传播。
    Propagation.REQUIRES_NEW 使用一个新的事务,如果当前没有事务就新建一个事务,如果有事务则将当前事务挂起再新建事务。新的事物和原事务没有任何关系,是两个独立的事务,外层事务出错回滚之后不能回滚内层事务的结果,内层事务失败回滚外层捕获到也可以忽略回滚操作。
    Propagation.SUPPORTS 支持当前事务,如果当前没有事务则以非事务方式执行。
    Propagation.MANDATORY 支持当前事务,如果当前没有事务则抛出异常。必须在有外部事务的方法内执行。
    Propagation.NOT_SUPPORTED 不支持事务,如果当前有事务则将当前事务挂起再执行
    Propagation.NEVER 以非事务方式执行,如果当前存在事务则抛出异常。
    Propagation.NESTED 如果没有活动事务,则按REQUIRED方式执行。如果一个活动的事务存在,则运行在一个嵌套的事务中,内部事务是一个依赖于外部事务的子事务,此时外部事务拥有多个可以单独回滚的保存点。内部事务的回滚不会对外部事务造成影响。内部事务不能单独commit和rollback,会随外部事务一起commit或rollback。

    NESTED 事务嵌套
      NESTED和REQUIRES_NEW的却别在于,如果外部方法存在事务,当内部方法是NESTED时,会先在外部事务产生一个savepoint,然后开启一个子事务,子事务是依赖于外部事务的,外部事务commit子事务也随之commit,外部事务rollback子事务也会rollback,子事务内发生异常,会先回滚到之前的savepoint,外部事务捕获到子事务异常后可以根据真实需求来做后续处理;而REQUIRES_NEW在外部方法存在事务时,子方法会先将外部事务挂起,然后新建一个完全独立的事务来执行,内部事务和外部事务互不影响。

    4. Spring中的隔离级别

      在使用@Transactional注解时,默认隔离级别为Isolation isolation() default Isolation.DEFAULT;

        @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)
        public void updateLog(BatchPreviousEntity bpe) {
            ...
        }
    
    常量名称 解释
    Isolation.DEFAULT 是PlatformTransactionManager的默认隔离级别,使用数据库默认的事务隔离级别。另外4个与数据库的隔离级别相对应。
    Isolation.READ_UNCOMMITTED 读未提交
    Isolation.READ_COMMITTED 读已提交
    Isolation.REPEATABLE_READ 可重复读
    Isolation.SERIALIZABLE 串行化
  • 相关阅读:
    git简单使用
    Kafka初入门简单配置与使用
    Hbase简单配置与使用
    Oozie简单配置与使用
    Flume初入门简单配置与使用
    sqoop简单配置与使用
    Android基础系列合集
    Java 基础系列合集
    TCP 和 UDP 区别
    http get和post区别
  • 原文地址:https://www.cnblogs.com/Qkxh320/p/spring_06.html
Copyright © 2011-2022 走看看