赞
踩
在名为Spring_Demo的项目中创建名为“Transaction”的普通Java模块,并在该模块中创建名为“lib”的包用于存放该模块所需的所有jar包。在该开发环境中与前一篇所需的开发环境一致,jar包参考前篇博客《Spring使用篇(十)—— Spring与MyBatis整合》。
在该模块中创建如下图所示的包结构,其中:
config包: 用于存放Java配置类;
controller包: 用于存放控制器类;
mapper包: 用于存放数据库操作接口;
pojo包: 用于存放POJO类;
service包: 用于存放业务接口;
service.impl包: 用于存放业务接口的实现类;
sql包: 用于存放MyBatis框架所需的数据库映射Mapper文件;
test包: 用于存放测试类
database-config.properties属性文件: 用于存储数据库四要素属性值;
log4j.properties: 为日志配置属性文件;
spring-config.xml: 为Spring框架配置文件;
SqlMapConfig.xml: 为MyBatis框架的核心配置文件。
其余开发环境包括数据库的创建,数据表的创建,POJO类的创建,Mapper接口的定义,数据库映射文件的编写以及Java配置类的基础配置,均与上一篇博客(《Spring使用篇(十)—— Spring与MyBatis整合》)相同。
在Spring中数据库事务是通过PlatformTransactionManager接口进行管理的。在Spring中,有多种事务管理器,由于目前在持久层常用MyBatis框架,因此常用的事务管理器是DataSourceTransactionManager(org.springframework.jdbc.datasource.DataSourceTransactionManager),它继承抽象事务管理器AbstractPlatformTransactionManager,而AbstractPlatformTransactionManager又实现了PlatformTransactionManager接口。
因此在Spring的XML配置文件中加入对事务管理器的配置,同时还需要加入XML的事务命名空间,spring-config.xml具体的配置修改如下:
<?xml version='1.0' encoding='UTF-8' ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--加载属性文件--> <context:property-placeholder location="database-config.properties" ignore-resource-not-found="true" /> <!-- 数据库连接池 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.database.driver}" /> <property name="url" value="${jdbc.database.url}" /> <property name="username" value="${jdbc.database.username}" /> <property name="password" value="${jdbc.database.password}" /> <!--连接池的最大数据库连接数 --> <property name="maxActive" value="255" /> <!--最大等待连接中的数量 --> <property name="maxIdle" value="5" /> <!--最大等待毫秒数 --> <property name="maxWait" value="10000" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:sqlMapConfig.xml" /> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.ccff.spring.transaction.mapper" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> <!-- 使用sqlSessionTemplateBeanName将覆盖sqlSessionFactoryBeanName的配置 --> <!-- <property name="sqlSessionTemplateBeanName" value="sqlSessionFactory"/> --> <!-- 指定标注才扫描成为Mapper --> <property name="annotationClass" value="org.springframework.stereotype.Repository" /> </bean> <!--定义事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> </beans>
在配置DataSourceTransactionManager事务管理器时注入了数据库连接池,这样Spring就知道此事已经将数据库事务委托给了事务管理器transactionManager管理了。
数据库事务的管理一定会涉及对数据库,尤其是数据库事务的知识,但本系列博客主要是对框架使用的学习,因此在这里只做简单说明。
并行事务的四大问题:第一,更新丢失:和别的事务读到相同的东西,各自写,自己的写被覆盖了。(谁写的快谁的更新就丢失了)。第二,脏读:读到别的事务未提交的数据。(万一回滚,数据就是脏的无效的了)。第三,不可重复读:两次读之间有别的事务修改。第四,幻读:两次读之间有别的事务增删。
数据的隔离级别主要有:读未提交、读已提交、可重复读和可序列化。对应的隔离级别分别为:READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ和SERIALIZABLE。各类隔离级别和产生的现象如下表所示:
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 可能 |
可序列化 | 不可能 | 不可能 | 不可能 |
而选取隔离级别的出发点在于两点:性能和数据一致性。数据库的隔离级别从读未提交到可序列化,系统性能直线下降。在实际工作中,注解@Transactional隔离级别的默认值为Isolation.DEFAULT,其含义是默认的,随数据库默认值的变化而变化。因为对不同的数据库而言,隔离级别的支持是不一样的。在MySQL中可支持4种隔离级别,而默认的是可重复读的隔离级别。而在Oracle中只支持读已提交和可序列化两种隔离级别,默认值是读已提交。
在Spring中传播行为的类型,是通过一个枚举类型去定义的,这个枚举类是org.springframework.transaction.annotation.Propagation,它定义了如下表所示的7种传播类型。
传播行为 | 含义 | 备注 |
---|---|---|
REQUIRED | 当方法调用时,如果不存在当前事务,那么就创建事务;如果之前的方法已经存在事务了,那么就沿用之前的事务 | 这是Spring默认的传播行为 |
SUPPORTS | 当方法调用时,如果不存在当前事务,那么不启用事务;如果存在当前事务,那么就沿用当前事务 | —— |
MANDATORY | 方法必须在事务内运行 | 如果不存在当前事务,那么就抛出异常 |
REQUIRES_NEW | 无论是否存在当前事务,方法都会在新的事务中运行 | 也就是事务管理器会打开新的事务运行该方法 |
NOT_SUPPORTED | 不支持事务,如果不存在当前事务也不会创建事务;如果存在当前事务,则挂起它,纸质该方法结束后才恢复当前事务 | 适用于那些不需要事务的SQL |
NEVER | 不支持事务,只有在没有事务的环境中才能运行它 | 如果存在当前事务,那么就抛出异常 |
NESTED | 嵌套事务,也就是调用方法如果抛出异常只回滚自己内部执行的SQL,而不回滚主方法的SQL | 它的实现存在两种情况,如果当前数据库支持保存点(savepoint),那么它就会在当前事务上使用保存点技术;如果发生异常则将方法内执行的SQL回滚到保存点上,而不是全部回滚,否则就等同于REQUIRES_NEW创建新的事务运行方法代码 |
7种传播行为中,最常用的是REQUIRED,也是默认的传播行为。对于那些不支持事务的方法我们使用得不多,一般而言,我们还比较关注的是REQUIRES_NEW和NESTED。
在Spring中可以使用编程式事务与声明式事务。如今,编程式事务几乎不用了,因为它会产生荣誉,代码可读性差,因此这里只介绍声明式事务。声明式事务又可以分为XML配置和注解事务,但XML方式也已经不常用了,目前主流方法是注解@Transactional,因此本篇仅介绍使用注解@Transactional。
声明式事务是一种约定型的事务,在大部分情况下,当使用数据库事务时,大部分的场景是在代码中发生了异常时,需要回滚事务,而不发生异常时则是提交事务,从而保证数据库数据的一致性。从这点出发,Spring给了一个约定,如果使用的是声明式事务,那么当你的业务方法不发生异常(或者发生异常,但该异常也被配置信息允许提交事务)时,Spring就会让事务管理器提交事务,而发生异常(并且该异常不被你的配置信息所允许提交事务)时,则让事务管理器回滚事务。
声明式事务允许自定义事务接口——TransactionDefinition,它可以由XML或者注解@Transactional进行配置,本篇主要讨论注解@Transactional,其配置项如下表所示:
配置项 | 含义 | 备注 |
---|---|---|
value | 定义事务管理器 | 它是Spring IoC容器里的一个Bean id,这个Bean需要实现接口PlatformTransactionManager |
transactionManager | 同上 | 同上 |
isolation | 隔离级别 | 这是一个数据库在多个事务同时存在时的概念,默认值取数据库默认的隔离级别 |
propagation | 传播行为 | 传播行为是方法之间调用的问题,默认值为Propagation.REQUIRED |
timeout | 超时时间 | 单位为秒,当超时时,会引发异常,默认会导致事务回滚 |
readOnly | 是否开启只读事务 | 默认值为false |
rollbackFor | 回滚事务的异常类定义 | 也就是只有当方法产生所定义异常时,才回滚事务,否则就提交事务 |
rollbackForClassName | 回滚事务的异常类名定义 | 同rollbackFor,只是使用类名称定义 |
noRollbackFor | 当产生哪些异常不回滚事务 | 当产生所定义的异常时,Spring会继续提交事务 |
noRollbackForClassName | 同noRollbackFor | 同noRollbackFor,只是使用类的名称定义 |
当了解了注解@Transactional的配置项后,有两种方式开启该注解。第一种是在Spring的配置XML文件spring-config.xml中通过如下配置开启,就可以使用注解@Transactional配置事务了,具体修改如下:
<?xml version='1.0' encoding='UTF-8' ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--加载属性文件--> <context:property-placeholder location="database-config.properties" ignore-resource-not-found="true" /> <!-- 数据库连接池 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${jdbc.database.driver}" /> <property name="url" value="${jdbc.database.url}" /> <property name="username" value="${jdbc.database.username}" /> <property name="password" value="${jdbc.database.password}" /> <!--连接池的最大数据库连接数 --> <property name="maxActive" value="255" /> <!--最大等待连接中的数量 --> <property name="maxIdle" value="5" /> <!--最大等待毫秒数 --> <property name="maxWait" value="10000" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="configLocation" value="classpath:sqlMapConfig.xml" />
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。