Spring JdbcTemplate
在spring中为了更加方便的操作JDBC,在JDBC的基础之上定义了一个抽象层,目的是为不同类型的JDBC操作提供模板方法,也就是对不同数据库提供不同模板方法,每个模板方法都能控制整个过程,并允许覆盖过程中的特定任务,通过这种方式,可以尽可能保留灵活性,将数据库存取的工作量降到最低。
Spring事物
Spring事务的本质其实就是通过spring来操作数据库的事务,数据库没有事务支持的话,spring是无法提供事务功能的。Spring事务使用AOP的机制实现,会在@Transactional注解修饰的方法前后分别织入开启事务的逻辑,以及提交或回滚的逻辑。
ACID属性
原子性(Atomicity)
: 事务是一个原子操作单元,其对数据的修改,要么全都执行,要么全都不执行。
一致性(Consistent)
: 在事务开始和完成时,数据都必须保持一致状态。
隔离性(Isolation)
: 事务处理过程中的状态对其他事务是不可见的。
持久性(Durable)
: 事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。
Spring在事务控制方面,主要分为两类
编程式事务
:在代码中直接加入处理事务的逻辑,显式的调用beginTransaction()、commit()、rollback()等事务管理相关的方法。
声明式事物
:在方法的外部添加注解或者直接在配置文件中定义,将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。spring的AOP可以完成此功能:事务管理代码的固定模式作为一种横切关注点,通过AOP方法模块化,进而实现声明式事务。
声明式事务配置
XML方式:
<!--jdbcTemplate.xml-->
<!--配置事务管理器的bean-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--基于xml配置的事务:依赖tx名称空间和aop名称空间
1、spring中提供事务管理器(切面),配置这个事务管理器
2、配置事务方法
3、告诉spring哪些方法是事务方法(事务切面按照我们的切入点表达式去切入事务方法)-->
<bean id="bookService" class="cn.test.service.BookService"></bean>
<aop:config>
<aop:pointcut id="txPoint" expression="execution(* cn.tulingxueyuan.service.*.*(..))"/>
<!--事务建议:advice-ref:指向事务管理器的配置-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="txPoint"></aop:advisor>
</aop:config>
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--事务属性-->
<tx:attributes>
<!--指明哪些方法是事务方法-->
<tx:method name="*"/>
<tx:method name="checkout" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"></tx:method>
</tx:attributes>
</tx:advice>
注解方式:@Transactional
使用此注解需要在配置类上加@EnableTransactionManagement注解来开启注解驱动
并且需保证已经配置了至少一个PlatformTransactionManager的Bean,否则会报错
@Transactional 标记在类上面表示当前类所有方法都有事务控制
@Transactional 标记在方法则只是当前方法有事务控制
@Transactional默认回滚的的异常是RuntimeException,如果需要支持回滚Exception异常要用@Transactional(rollbackFor = Exception.class)
如果类和方法都存在@Transactional会以方法的为准。如果方法上面没有@Transactional会以类上面的为准
建议:
@Transactional写在方法上面,控制粒度更细,
@Transactional写在业务逻辑层上,因为只有业务逻辑层才会有嵌套调用的情况。
事务配置的属性
isolation:设置事务的隔离级别
propagation:事务的传播行为
noRollbackFor:定义哪些异常事务可以不回滚
noRollbackForClassName:填写的参数是全类名
rollbackFor:定义哪些异常事务需要回滚
rollbackForClassName:填写的参数是全类名
readOnly:设置事务是否为只读事务
timeout:事务超出指定执行时长后自动终止并回滚,单位是秒
Spring的事务传播机制
当出现多个事务嵌套的场景发生时,Spring事务的处理会变得复杂一些,需要考虑嵌套事务下的提交顺序,以及回滚顺序,对此,Spring提供了多种的传播机制:
REQUIRED
:默认值,支持当前事务,如果没有事务会创建一个新的事务
SUPPORTS
:支持当前事务,如果没有事务的话以非事务方式执行
MANDATORY
:支持当前事务,如果没有事务抛出异常
REQUIRES_NEW
:创建一个新的事务并挂起当前事务
NOT_SUPPORTED
:以非事务方式执行,如果当前存在事务则将当前事务挂起
NEVER
:以非事务方式进行,如果存在事务则抛出异常
NESTED
:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作
当子方法事务的传播机制为REQUIRED时:
当子方法事务的传播机制为NESTED时:
Spring事务嵌套默认传播机制下最佳实践
1、外部方法无事务注解,内部方法添加REQUIRED事务传播类型:
内部方法抛出异常,内部方法事物回滚,不会影响外部方法,外部方法会执行成功。
2、外部方法添加REQUIRED事务传播类型,内部方法无事务注解:
内部方法抛出异常,会影响外部方法的执行,导致外部事务回滚。
3、外部方法、内部方法都添加REQUIRED事务传播类型:
内部方法抛出异常,会影响外部方法的执行,内外事务都回滚。
4、外部方法添加REQUIRED事务传播类型,内部方法添加NOT_SUPPORTED事务传播类型
内部方法抛出异常,外部方法会回滚,内部方法不会回滚;
外部方法抛出异常,内部方法执行成功,内部方法会提交;
内部方法永远不会回滚。
Spring事务失效场景
1、数据库不支持事务
2、事务方法未被Spring管理,也就是事务方法所在的类没有加载到Spring IOC容器中
3、方法没有被public修饰
4、方法的事务传播类型不支持事务(NOT_SUPPORTED、NEVER)。
5、使用了try-catch代码块将异常捕捉了,没有向上抛出异常,事务不会回滚。
6、标注错误的异常类型,例如Spring事务默认回滚类型是RuntimeException类型,如果没有制定回滚的类型,抛出的错误不是RuntimeException类型,则无法回滚。