本文讲解 Spring 注入映射器及事务功能的实现.
注入映射器实现
MyBatis 可以使用 SqlSession 的 getMapper (Class<T> type ) 方法, 根据指定的映射器和映射文件直接生成实现类. 这样不必自行编写映射器的实现类, 就可以调用映射器的方法进行功能实现.
SqlSessionTemplate 也有对应的 getMapper() 方法, 利用 MyBatis-Spring 提供的组件, 可以不必每次调用 getMapper() 方法, 而是通过配置的方式直接为业务对象注入映射器实现. 对于不包含其他非 MyBatis 的工作的数据访问操作, 这是首选的做法.
使用 MapperScannerConfigurer 注入映射器
SQL 映射文件中须遵循以下命名原则:
1) 映射的命名空间和映射器接口的名称相同.
2) 映射元素的 id 和映射器接口的方法相同.
首先, 有关 DAO 接口的实现类可以删除了.
配置注入映射器的代码如下:
- <!-- 配置 DAO -->
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="basePackage" value="cn.xxxx.mapper" />
- </bean>
basePackage 属性指定了扫描的基准包, MapperScannerConfigurer 将递归扫描基准包下所有接口. 如果它们在 SQL 映射文件中定义过, 则将它们动态注册为 MapperFactoryBean .
注意:
1)basePackage 属性中可以包含多个包名. 多个包名之间使用逗号或分号隔开.
2)MapperScannerConfigurer 创建的所有映射器实现都会被自动注入 SqlSessionFactory 实例.
3) 若环境中配置了多个 SqlSessionFactory 实例, 自动装载将无法进行. 此时应显式指定所依赖的 SqlSessionFactory 实例. 下面是示例
- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
- <property name="basePackage" value="cn.bdqn.mapper" />
- </bean>
映射器被注册到 Spring 的容器时, Spring 会根据其接口名称为其命名, 默认规则是首字母小写的非完全限定类名. 如 UserMapper 类型组件会被默认命名为 userMapper.
在业务逻辑实现类中使用 @Resource 或 @AutoWried 注解实现对业务组件的依赖注入.
Spring 配置文件中需要引入 context 命名空间, 并添加一行配置代码
- <!-- 配置扫描注解定义的业务 Bean -->
- <context:component-scan base-package="cn.xxxx.service" />
声明式事务
首先了解一下事务相关的理论知识
原子性 (Atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成, 要么完全不起作用.
一致性 (Consistency): 事务在完成时, 必须是所有的数据都保持一致状态.
隔离性 (Isolation): 并发事务执行之间无影响, 在一个事务内部的操作对其他事务是不产生影响, 这需要事务隔离级别来指定隔离性.
持久性 (Durability): 一旦事务完成, 数据库的改变必须是持久化的.
事务并发所可能存在的问题:
脏读: 一个事务读到另一个事务未提交的更新数据.
不可重复读: 一个事务两次读同一行数据, 可是这两次读到的数据不一样.
幻读: 一个事务执行两次查询, 但第二次查询比第一次查询多出了一些数据行.
丢失更新: 撤消一个事务时, 把其它事务已提交的更新的数据覆盖了.
JDBC 定制了五种事务隔离级别处理事务并发:
TRANSACTION_NONE JDBC : 驱动不支持事务
TRANSACTION_READ_UNCOMMITTED: 允许脏读, 不可重复读和幻读.
TRANSACTION_READ_COMMITTED: 禁止脏读, 但允许不可重复读和幻读.
TRANSACTION_REPEATABLE_READ: 禁止脏读和不可重复读, 单运行幻读.
TRANSACTION_SERIALIZABLE: 禁止脏读, 不可重复读和幻读.
隔离级别越高, 意味着数据库事务并发执行性能越差, 能处理的操作就越少.
JDBC 规范虽然定义了事务的以上支持行为, 但是各个 JDBC 驱动, 数据库厂商对事务的支持程度可能各不相同.
出于性能的考虑我们一般设置 TRANSACTION_READ_COMMITTED.
通过 Spring, 我们无需处理获得连接, 关闭连接, 事务提交和回滚等这些操作, 使得我们把更多的精力放在处理业务上.
事实上 Spring 并不直接管理事务, 而是提供了多种事务管理器. 它们将事务管理的职责委托给 Hibernate 或者 JTA 等持久化机制所提供的相关平台框架的事务来实现.
本文主要讲解 Spring 提供的声明式事务处理机制, 它基于 AOP 实现, 无须编写任何事务管理代码, 所有的工作全在配置文件中完成.
配置
需要用到 tx 和 aop 两个命名空间, 所以首先要导入这两个命名空间
- <?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-3.0.xsd
- http://www.springframework.org/schema/tx
- http://www.springframework.org/schema/tx/spring-tx.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-3.0.xsd">
- <beans/>
配置一个事务管理器组件, 这里使用 Spring 提供的事务管理器类 DataSourceTransactionManager
- <!-- 定义事务管理器 -->
- <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource" />
- </bean>
这里的 dataSource 是上文中配置的数据源组件.
可以通过 <tx :advice> 标签配置事务增强, 设定事务的属性, 为不同的业务方法指定具体的事务规则.
- <!-- 为指定事务管理器设置事务属性 -->
- <tx:advice id="txAdvice" transaction-manager="transactionManager">
- <!-- 定义属性, 声明事务规则 -->
- <tx:attributes>
- <tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT"
- read-only="false" />
- <tx:method name="del*" propagation="REQUIRED" isolation="DEFAULT"
- read-only="false" />
- <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT"
- read-only="false" />
- <tx:method name="query*" propagation="NEVER" read-only="true" />
- <tx:method name="get*" propagation="NEVER" read-only="true" />
- </tx:attributes>
- </tx:advice>
transaction-manager 属性引用一个事务管理器 Bean
- <tx:attributes> 子标签用来定制事务属性. 事务属性通过 <tx:method> 标签进行设置.
- <tx:method> 标签中 name 属性是必须的, 用于指定匹配的方法, 可以使用通配符 ( * ). 其他属性均为可选, 用于指定具体的事务规则.
Spring 事务的传播属性
名称 | 值 | 解释 |
---|---|---|
PROPAGATION_REQUIRED | 0 | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择,也是 Spring 默认的事务的传播。 |
PROPAGATION_SUPPORTS | 1 | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 2 | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 3 | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 4 | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 5 | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 6 | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与 PROPAGATION_REQUIRED 类似的操作。 |
Spring 事务的隔离级别
名称 | 值 | 解释 |
---|---|---|
ISOLATION_DEFAULT | -1 | 这是一个 PlatfromTransactionManager 默认的隔离级别,使用数据库默认的事务隔离级别。另外四个与 JDBC 的隔离级别相对应 |
ISOLATION_READ_UNCOMMITTED | 1 | 这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。 |
ISOLATION_READ_COMMITTED | 2 | 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。 |
ISOLATION_REPEATABLE_READ | 4 | 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻读。 |
ISOLATION_SERIALIZABLE | 8 | 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻读。 |
最后是定义切面
- <!-- 定义切面 -->
- <aop:config>
- <!-- 定义切入点 -->
- <aop:pointcut expression="execution(* cn.xxxx.service..*.*(..))" id="pointcutTransaction" />
- <!-- 将事务增强与切入点组合 -->
- <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcutTransaction"/>
- </aop:config>
使用注解实现声明式事务处理
Spring 支持使用注解配置声明式事务, 所使用的注解是 @Transactional
首先需要在 Spring 配置文件中添加对注解配置的事务的支持
<tx:annotation-driven transaction-manager="transactionManager"/>
在 Service 业务逻辑实现类中添加注解
- @Transactional
- @Service("userSerivce")
- public class UserService implements IUserService{
- @Autowired
- @Qualifier("userMapper")
- private UserMapper userMapper;
- @Override
- @Transactional(propagation=Propagation.SUPPORTS)
- public List<USER> queryUsers() {
- return userMapper.queryUsers();
- }
- }
注意:
1) 在类上添加 @Transactional 注解可为该类的所有业务方法统一添加事务处理. 如果某一业务方法需要采用不同的事务规则, 可以在该业务方法上添加 @Transactional 注解单独进行设置.
2) 不建议在查询方法上使用事务, 会影响查询的性能.
使用注解配置事务时, 所使用的属性与在 Spring 配置文件中使用的属性一致.
来源: https://www.cnblogs.com/xiaotie666/p/LiujinSpring_MyBatis2Summary.html