今天补充一下Spring里的事务管理
其实就是一句话,做一系列的事情,要么都成功,要么都失败。之前接触事务的概念主要是在数据库中,事务涉及到数据的一致性问题,要确保完整性和一致性。
不可重复度和幻读的主要区别是,不可重复读的重点是修改,幻读的重点在于新增或者删除。
讲到这些不讲隔离级别那肯定不行的
其实这里的事务管理,就是“按照给定的事务规则来执行提交或者回滚操作”。
Spring本身是不直接管理实务的,而是提供了多种事务管理器,将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的的相关平台框架的事务来实现。
Spring事务管理的接口是:org.springframework.transaction.PlatformTransactionManager
通过这个接口,Spring给各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但具体如何实现就是各个平台自己的事情了。
PlatformTransactionManager接口中有三个方法:
package org.springframework.transaction; import org.springframework.lang.Nullable; public interface PlatformTransactionManager extends TransactionManager { TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; //根据指定的传播行为,返回当前活动的事务或创建一个新事务。 void commit(TransactionStatus status) throws TransactionException; //使用事务目前的状态提交事务 void rollback(TransactionStatus status) throws TransactionException; //执行的事务进行回滚 }我们在JDBC或者Mybatis里进行数据持久化操作时,我们的xml配置通常如下:
<!--配置声明式事务--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--数据源--> <property name="dataSource" ref="dataSource"/> </bean>可以看到在PlatformTransactionManager源码里的getTransaction方法中返回了一个事务,这个方法里的参数就是TransactionDefinition类,这个类定义了一些基本的事务属性。
事务属性的种类主要分为:传播行为、隔离级别、只读、事务超时
我们点进TransactionDefinition源码可以看到它的结构
传播行为定义了被调用方法的事务边界
传播行为意义PROPERGATION_MANDATORY表示方法必须运行在一个事务中,如果当前事务不存在,就抛出异常PROPAGATION_NESTED表示如果当前事务存在,则方法应该运行在一个嵌套事务中。否则,它等价于 PROPAGATION_REQUIREDPROPAGATION_NEVER表示方法不能运行在一个事务中,否则抛出异常PROPAGATION_NOT_SUPPORTED表示方法不能运行在一个事务中,如果当前存在一个事务,则该方法将被挂起PROPAGATION_REQUIRED表示当前方法必须运行在一个事务中,如果当前存在一个事务,那么该方法运行在这个事务中,否则,将创建一个新的事务PROPAGATION_REQUIRES_NEW表示当前方法必须运行在自己的事务中,如果当前存在一个事务,那么这个事务将在该方法运行期间被挂起PROPAGATION_SUPPORTS表示当前方法不需要运行在一个是事务中,但如果有一个事务已经存在,该方法也可以运行在这个事务中这里的隔离级别跟mysql中有相同的地方也有不同的地方,我们在操作数据的时候会有3个副作用,分别是脏读、不可重复读、幻读。为了避免这三个现象发生,在标准的SQL里定义了四种隔离级别,上面也说了,分别是未提交读、已提交读、可重复读、序列化。但是在Spring的事务里我们可以看到提供了5种隔离级别来对应SQL种定义的4种隔离级别
隔离级别意义ISOLATION_DEFAULT使用后端数据库默认的隔离级别ISOLATION_READ_UNCOMMITTED允许读取未提交的数据(对应未提交读),可能导致脏读、不可重复读、幻读ISOLATION_READ_COMMITTED允许在一个事务中读取另一个已经提交的事务中的数据(对应已提交读)。可以避免脏读,但是无法避免不可重复读和幻读ISOLATION_REPEATABLE_READ一个事务不可能更新由另一个事务修改但尚未提交(回滚)的数据(对应可重复读)。可以避免脏读和不可重复读,但无法避免幻读ISOLATION_SERIALIZABLE这种隔离级别是所有的事务都在一个执行队列中,依次顺序执行,而不是并行(对应可序列化)。可以避免脏读、不可重复读、幻读。但是这种隔离级别效率很低,因此,除非必须,否则不建议使用。如果在一个事务中所有关于数据库的操作都是只读的,也就是说,这些操作只读取数据库中的数据,而并不更新数据,那么应将事务设为只读模式( READ_ONLY_MARKER ) , 这样更有利于数据库进行优化 。
因为只读的优化措施是事务启动后由数据库实施的,因此,只有将那些具有可能启动新事务的传播行为 (PROPAGATION_NESTED 、 PROPAGATION_REQUIRED 、 PROPAGATION_REQUIRED_NEW) 的方法的事务标记成只读才有意义。
如果使用 Hibernate 作为持久化机制,那么将事务标记为只读后,会将 Hibernate 的 flush 模式设置为 FULSH_NEVER, 以告诉 Hibernate 避免和数据库之间进行不必要的同步,并将所有更新延迟到事务结束。
如果一个事务长时间运行,为了避免系统资源的浪费,我们应该给这个事务设置一个有效时间,使其等待数秒后自动回滚。与设置“只读”属性一样,事务有效属性也需要给那些具有可能启动新事物的传播行为的方法的事务标记成只读才有意义。
这里面也就几个方法干脆一起说说
TransactionDefinition内的方法
public interface TransactionDefinition { //返回事务的传播行为 default int getPropagationBehavior() { return PROPAGATION_REQUIRED; } //返回事务的隔离级别,事务管理器根据他来控制另一个事务可以看到本事务的数据范围 default int getIsolationLevel() { return ISOLATION_DEFAULT; } //返回事务需要在多少秒内完成 default int getTimeout() { return TIMEOUT_DEFAULT; } //返回是否优化为只读事务 default boolean isReadOnly() { return false; } //返回事务名字 @Nullable default String getName() { return null; } //返回一个不可修改的TransactionDefinition使用默认值。 static TransactionDefinition withDefaults() { return StaticTransactionDefinition.INSTANCE; } }还是刚才的PlatformTransactionManager源码,你瞧getTransaction的返回值是什么
可以看到返回的是一个TransactionStatus对象。
源码看看
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable { /** * 返回该事务是否在内部带有保存点, * 也就是说,已创建为基于保存点的嵌套事务。 * * 此方法主要用于诊断目的,另外还有{@link #isNewTransaction()}。 * 对于自定义保存点的编程处理,使用{@link SavepointManager}提供的操作。 * */ boolean hasSavepoint(); /** * 将底层会话刷新到数据存储(如果适用): * 例如,所有受影响的Hibernate/JPA会话。 * * 这实际上只是一个提示,如果底层事务管理器没有刷新的概念,则可能是一个no-op。 * 刷新信号可能应用于主资源或事务同步,这取决于底层资源。 */ @Override void flush(); }老版的spring中的TransactionStatus里还有isNewTransaction()、setRollbackOnly()、isCompleted()等方法,新版直接在接口的实现类AbstractTransactionStatus中实现了,
声明式事务:AOP
编程式事务:需要在代码中,进行事务管理
在spring-dao中加入配置
<!--配置声明式事务,即事务管理器--> <bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--结合AOP实现事务的织入--> <!--配置事务(通知/的类)--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!--给哪些方法配置事务--> <!--配置事务的传播特性:new propagation:传播 默认为:REQUIRED--> <tx:attributes> <tx:method name="add" propagation="REQUIRED"/> <tx:method name="delete" propagation="REQUIRED"/> <tx:method name="select" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--配置事务切入--> <aop:config> <aop:pointcut id="txPointCut" expression="execution(* com.feng.mapper.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/> </aop:config>如果对实例感兴趣的可以看看guide哥这篇
https://blog.csdn.net/qq_34337272/article/details/80419316
