目录
一、Spring结合mybatis
二、业务层添加声明式事务
1、事务的传播机制
2、事务的四大特性
3、事务的隔离级别
4、事务属性
一、Spring结合mybatis
1.创建Web工程,导入Spring和MyBatis的相关依赖
<!-- spring+mybatis整合 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.9</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
2.建立开发目录结构,创建实体类
3.创建数据访问接口和SQL映射语句文件
4.使用Spring配置文件配置数据源
数据源的相关配置信息放到properties文件中维护
采用PropertySourcesPlaceholderConfigurer类加载properties文件
Spring配置文件中采用${xxx}方式引用properties文件中的键值对数据
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/smbms?useUnicode=true&characterEncoding=utf-8&useSSL=false
user=root
password=root
5.使用Spring配置文件创建SqlSessionFactory
<!--数据源的相关配置db.properties-->
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="classpath:db.properties"/>
</bean>
<!--创建数据源的bean-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
<!-- 创建sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
<property name="mapperLocations">
<list>
<value>classpath:mybatis/mapper/*.xml</value>
</list>
</property>
</bean>
6.配置MyBatis应用配置文件
<typeAliases>
<package name="cn.smbms.pojo"/>
</typeAliases>
7.创建数据访问接口的实现类
8.在Spring配置文件中注入SqlSessionTemplate
<!--创建sqlSessionTemplate-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!--创建dao对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.smbms.mapper"/>
</bean>
<context:component-scan base-package="cn.smbms.service"/>
9.创建业务接口和业务实现类
10.使用单元测试类测试
MapperScannerConfigurer 与@Autowired注解或@Resource注解配合使用,自动创建映射器实现并注入给业务组件,能够最大限度地减少DAO组件与业务组件的编码和配置工作
@Service("userService")
public class UserServiceImpl implements UserService {
@Autowired // 或 @Resource
private UserMapper userMapper;
//……代码省略
}
<!-- 省略数据源、 SqlSessionFactoryBean的相关配置 -->
<!-- 配置DAO -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.smbms.dao" />
</bean>
<!-- 配置扫描注解定义的业务Bean -->
<context:component-scan base-package="cn.smbms.service" />
二、业务层添加声明式事务
1、事务的传播机制
1、REQUIRED(需要)(默认)
定义:如果有事务则加入事务,如果没有事务,则创建一个新的(默认值)
回滚机制:如果调用方有事务,则加入,所以是同一个事务,有异常则一起回滚,如果调用方没事务,则创建
一个新的事务
2、REQUIRES_NEW(需要新的)
定义:不管是否存在事务,都创建一个新的事务,两个事务之间没有关系
回滚机制:由于调用方和被调方都属于不同的事务,所以回滚互不影响
3、SUPPORTS(支持)
定义:调用方有事务则直接用,如果没有则不使用事务
回滚机制:调用方有事务则加入,由于是同一个事务,一旦发生异常,则整体回滚
调用方没事务,被调方也不会使用事务,则不存在回滚
4、NOT_SUPPORTED(不支持)
定义:Spring不为当前方法开启事务,相当于没有事务
回滚机制:不论调用方是否有事务,被调方都不使用事务,则不存在回滚
5、NEVER(从不)
定义:必须在一个没有的事务中执行,否则报错
回滚机制: 如果调用方开启了事务,则被调方会抛出异常,被调方回滚
6、MANDATORY(强制)
定义:必须在一个已有的事务中执行,否则报错
回滚机制:如果调用方开启事务,则加入同一个事务,不论谁异常,都会整体回滚
如果调用方没开启事务,则抛出异常,被调方回滚
7、NESTED(嵌套)
定义:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似
的操作
若调用者方法有开启事务。此时NESTED会开始一个 "嵌套的" 事务, 它是已经存在事务的一个真
正的子事务。 嵌套事务开始执行时, 它将取得一个 savepoint。 如果这个嵌套事务失败, 我们将
回滚到此 savepoint。 嵌套事务是外部事务的一部分, 只有外部事务结束后它才会被提交。
回滚机制:主事务和嵌套事务属于同一个事务,嵌套事务出错回滚不会影响到主事务,主事务回滚
会将嵌套事务一起回滚了
2、事务的四大特性
事务具有4个基本特征,分别是:原子性(Atomicity)、一致性(Consistency)、隔离性
(Isolation)、持久性(Duration),简称ACID
① 原子性(Atomicity)
事务的原子性是指事务必须是一个原子的操作序列单元。事务中包含的各项操作在一次执行过程
中,只允许出现两种状态之一,要么都成功,要么都失败
任何一项操作都会导致整个事务的失败,同时其它已经被执行的操作都将被撤销并回滚,只有所有
的操作全部成功,整个事务才算是成功完成
② 一致性(Consistency)
事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行
之后,数据库都必须处以一致性状态。
比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱
③ 隔离性(Isolation)
事务的隔离性是指在并发环境中,并发的事务是互相隔离的,一个事务的执行不能被其它事务干
扰。也就是说,不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。
一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰
的
④ 持久性(Duration)
事务的持久性是指事务一旦提交后,数据库中的数据必须被永久的保存下来。即使服务器系统崩溃
或服务器宕机等故障。只要数据库重新启动,那么一定能够将其恢复到事务成功结束后的状态
3、事务的隔离级别
Read uncommitted:读未提交
顾名思义,就是一个事务可以读取另一个未提交事务的数据。
Read committed:读提交
顾名思义,就是一个事务要等另一个事务提交后才能读取数据。
Repeatable read:重复读
就是在开始读取数据(事务开启)时,不再允许修改操作
什么时候会出现幻读?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描
FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即
新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),
发现花了1.2万元,似乎出现了幻觉,这就是幻读。
Serializable 序列化
Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重
复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
4、事务属性
属性 | 说明 |
timeout | 事务超时时间,允许事务运行的最长时间,以秒为单位。默认值为-1,表示不超时 |
read-only | 事务是否为只读,默认值为false |
rollback-for | 设定能够触发回滚的异常类型 Spring默认只在抛出runtime exception时才标识事务回滚 可以通过全限定类名指定需要回滚事务的异常,多个类名用逗号隔开 |
no-rollback-for | 设定不触发回滚的异常类型 Spring默认checked Exception不会触发事务回滚 可以通过全限定类名指定不需回滚事务的异常,多个类名用英文逗号隔开 |
在Spring配置文件中配置事务管理类,并添加对注解配置的事务的支持
<bean id="txManager" class="org.springframework.jdbc.datasource
.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
@Transactional
@Service("userService")
public class UserServiceImpl implements UserService {
……
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> findUsersWithConditions(User user) {
// 省略实现代码
}}
使用注解实现事务处理
属性 | 类型 | 说明 |
propagation | 枚举型:Propagation | 可选的传播性设置。使用举例: @Transactional( propagation=Propagation.REQUIRES_NEW) |
isolation | 枚举型:Isolation | 可选的隔离性级别。使用举例: @Transactional( isolation=Isolation.READ_COMMITTED) |
readOnly | 布尔型 | 是否为只读型事务。使用举例:@Transactional(readOnly=true) |
timeout | int型(以秒为单位) | 事务超时。使用举例:Transactional(timeout=10) |
使用注解实现事务处理
属性 | 类型 | 说明 |
rollbackFor | 一组 Class 类的实例,必须是Throwable的子类 | 一组异常类,遇到时 必须 回滚。使用举例:@Transactional( rollbackFor={SQLException.class}),多个异常用逗号隔开 |
rollbackForClassName | 一组 Class 类的名字,必须是Throwable的子类 | 一组异常类名,遇到时 必须 回滚。使用举例:@Transactional( rollbackForClassName={ "SQLException"}),多个异常用逗号隔开 |
noRollbackFor | 一组 Class 类的实例,必须是Throwable的子类 | 一组异常类,遇到时 必须不 回滚 |
noRollbackForClassName | 一组 Class 类的名字,必须是Throwable的子类 | 一组异常类名,遇到时 必须不 回滚 |