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 串行化
原文地址:https://www.cnblogs.com/Qkxh320/p/spring_06.html