SSM Chapter 07 MyBatis 与 Spring 的整合 笔记
本章目标:
- 掌握Spring与MyBatis的集成
- 掌握使用SqlSessionTemplate实现整合
- 掌握使用MapperFactoryBean实现整合
- 掌握Spring的事务切面实现声明式事务处理
- 掌握使用注解实现声明式事务处理
技术内容:
我们已经学习了在MyBatis的基础知识,能够使用MyBatis 进行数据库操作.而Spring框架通过IoC.AOP等机制,能够对项目中的组件进行解耦合管理,建立一个低耦合的应用架构.这大大增强了系统开发和维护的灵活性,便于功能扩展.
这里我们将利用 Spring 对 MyBatis 进行整合,在对组件实现解耦的同时,还能使MyBatis框架的使用变得更加方便和简单. 还能使MyBatis框架的使用变得更加方便和简单.
此外,通过Spring提供的声明式事务等服务,能够进一步简化代码,减少开发工作量,提高开发效率
1 . Spring 对 MyBatis 的整合思路
作为Beans容器,Spring框架提供IoC机制,可以接管所有组件的创建工作并进行依赖管理. 因而整合的主要工作是把MyBatis框架中所涉及的核心组件配置到Spring容器中,交给Spring来创建和管理.
具体来说,业务逻辑对象依赖基于MyBatis技术实现的DAO对象,核心是获取SqlSession实例.要获得SqlSession实例,则需要依赖SqlSessionFactory实例. 而SqlSessionFactory是SqlSessionFactoryBuilder 依据MyBatis配置文件中的数据源,SQL映射文件等信息来构建的.
针对上述依赖关系,以往我们需要自行编码通过SqlSessionFactoryBuilder读取配置文件,构建SqlSessionFactory,进而获得SqlSession实例,满足业务对象对于数据访问的需要.随着Spring框架的引入,以上流程将全部移交给Spring,发挥Spring 框架 Bean容器的作用,接管组件的创建工作,管理组件的生命周期,并对组件之间的依赖关系进行解耦合管理.
2 . Spring 整合 MyBatis 的准备工作
我们以超市订单系统的功能为例 来完成 Spring 与 MyBatis 的整合
1. 在项目中加入Spring MyBatis 及整合相关的 jar 文件
在使用 Spring 整合MyBatis 之前,首先要下载与整合相关的jar文件.由于Spring 3 的开发在 MyBatis 3 官方发布前就结束了,Spring 开发团队不想发布一个基于非发布版本的MyBatis的整合支持,因此Spring 3 没有提供了对MyBatis 3 的支持.
为了使Spring 3 支持 MyBatis 3, MyBatis 团队按照Spring 集成ORM框架的统一风格开发了相关的组件,令开发者可以在Spring中集成使用MyBatis
整合时,项目中需要mybatis-spring的jar文件可以在官网上下载,官网地址:http://www.mybatis.org/spring/zh/index.html
官网文档也参考此地址
由于在整合中还有用到Spring的数据源支持 以及 事务支持,因此在项目中还需要添加 spring-jdbc 和 spring-tx两个jar文件,项目所需的jar文件如下:
- junit:junit:4.12
- log4j:log4j:1.2.17
- mysql:mysql-connector-java:5.1.38
- org.springframework:spring-context:5.1.5.RELEASE
- org.springframework:spring-aop:5.1.5.RELEASE
- org.springframework:spring-beans:5.1.5.RELEASE
- org.springframework:spring-core:5.1.5.RELEASE
- org.springframework:spring-expression:5.1.5.RELEASE
- org.aspectj:aspectjweaver:1.9.2
- commons-logging:commons-logging:1.2
- org.springframework:spring-test:5.1.5.RELEASE
- org.springframework:spring-jdbc:5.1.5.RELEASE
- org.springframework:spring-tx:5.1.5.RELEASE
- commons-dbcp:commons-dbcp:1.4
- commons-pool:commons-pool:1.6
- org.mybatis:mybatis:3.5.1
- org.mybatis:mybatis-spring:2.0.3
使用maven创建web项目,在pom.xml文件中加入如下的依赖:
<properties>
<spring.version>5.1.5.RELEASE</spring.version>
</properties>
<dependencyManagement>
<!--统一声明所有关于spring依赖的jar-->
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>${spring.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>
<!--mybatis-spring-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<!--spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<!--spring-jdbc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!--dbcp-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.6.0</version>
</dependency>
</dependencies>
分析 :
解决问题的步骤如下 :
- (1) 获取Spring 开发包 并为 工程添加Spring支持
- (2) 为业务层 和 数据访问层 设计接口,声明所需方法
- (3) 编写数据访问层UserDao的实现类,完成具体的持久化操作
- (4) 在业务实现类中声明 UserDao 接口类型的属性 , 并添加适当的构造方法为属性赋值
- (5) 在Spring的配置文件中,将DAO对象以构造注入的方式,赋值给业务实例中的UserDao类型的属性
- (6) 在代码中获取Spring配置文件中装配好的业务类对象,实现程序功能
2. 建立目录结构,创建实体类
在cn.foo.pojo包下创建实体类User.
public class User implements Serializable {
private Integer id; //用户id
private String userCode; //用户编码
private String userName; //用户名称
private String userPassword;//用户密码
private Integer gender; //性别
private Date birthday; //生日
private String phone; //联系方式
private String address; //地址
private Integer userRole; //用户角色id
private Integer createdBy; //创建者
private Date creationDate; //创建时间
private Integer modifyBy; //更新者
private Date modifyDate; //更新日期
//省略getter和setter方法 以及toString方法
}
3. 创建数据访问接口
在cn.foo.dao.user 包中创建 实体类 User 对应的 DAO接口 UserMapper.这里先添加一个查询所有用户信息的方法,其他方法在需要时添加.
public interface UserMapper {
/**
* 查询所有的用户列表
* @return
*/
List<User> getUserList();
}
4. 配置SQL映射文件
同样在cn.smbms.dao.user包中,为UserMapper配置SQL语句映射文件UserMapper.xml,实现指定的查询映射:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.foo.dao.user.UserMapper">
<!--查询用户列表-->
<select id="getUserList" resultType="user">
select * from smbms_user
</select>
</mapper>
5. 配置MyBatis 配置文件
编写MyBatis配置文件 mybatis-config.xml,设置所需参数
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<!-- 这个配置使全局的映射器启用或禁用缓存 -->
<setting name="cacheEnabled" value="true"/>
<!-- :设置驱动程序等待数据库响应的秒数 -->
<setting name="defaultStatementTimeout" value="5"/>
<!--
启用从经典数据库列名A_COLUMN到驼峰式经典Java属性名aColumn的自动映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--允许JDBC 生成主键。需要驱动器支持。如果设为了true,
这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 -->
<setting name="useGeneratedKeys" value="true"/>
</settings>
<!--为实体类指定别名-->
<typeAliases>
<package name="cn.foo.pojo"/>
</typeAliases>
</configuration>
注意 :
在上述代码中的MyBatis 配置文件内容 与之前的相比简单了许多,这是因为Spring 可以接管MyBatis配置信息的维护工作.这里我们选择把数据源配置和SQL映射信息转移至 Spring 配置文件中进行管理,以了解如何在Spring中配置MyBatis.
3 . 实现Spring 对 MyBatis 的整合
如前文所述,Spring需要依次完成加载MyBatis配置信息,构建SqlSessionFactory 和 SqlSession实例,完成对业务逻辑对象的依赖注入等工作.需要注意的是,这些工作大多以配置文件的方式实现,无须编写相关类,大大简化了开发且更容易维护.Spring配置文件中的主要配置如下:
3.1 配置数据源
对于任何持久化解决方案,数据库连接都是首先要解决的问题. 在Spring 中,数据源作为一个重要的组件,可以进行单独配置和维护.接下来我们将MyBatis配置中有关数据源的配置,转移到Spring 配置文件中进行维护.
配置DBCP
在Spring中配置数据源,首先要选择一种具体的数据源实现技术. 目前流行的数据源有 dbcp,c3p0,druid..等,它们都实现了连接池的功能.
这里以配置dbcp数据源为例进行讲解,其他的数据源配置方法与此类似,大家可以自行查阅官方网站以及相关资料进行学习. dbcp数据源隶属于 Apache Commons 项目,因此使用dbcp数据源就需要下载 相关的jar文件(这里包括两个,分别是: commons-dbcp-1.4.jar 和 commons-pool-1.4.jar).
当然如果是maven项目,则需要在pom.xml文件中加入相关的依赖,前文中我们已经加入了. 依赖代码如下:
<!--commons - dbcp-->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<!--commons-pool-->
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.6</version>
</dependency>
也可使用dbcp2 包含了以上两个依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.6.0</version>
</dependency>
建立Spring 配置文件 applicationContext-mybatis.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--配置数据源 BasicDataSource类实现了DataSource接口,可以用于DBCP连接池的简单使用-->
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/foo?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
</beans>
解释 BasicDataSource 配置如下:
参数 | 描述 |
---|---|
username | 传递给JDBC驱动的用于建立连接的用户名 |
password | 传递给JDBC驱动的用于建立连接的密码 |
url | 传递给JDBC驱动的用于建立连接的URL |
driverClassName | 使用的JDBC驱动的完整有效的java 类名 |
注意:因为url属性的值包含特殊符号 “ & “,所以赋值是使用了实体引用 “**&**
“,当然也可以使用**<![CDATA[]]>**
标记
其他配置可参考官网 :
http://commons.apache.org/proper/commons-dbcp/configuration.html
配置DriverManagerDataSource
使用Spring内置的连接池DriverManagerDataSource ,配置数据源, 代码如下:
<!--注入spring内置的数据源-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/foo?useUnicode=true&characterEncoding=utf-8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
参考官网如下:
3.2 配置SqlSessionFactoryBean
配置完数据源之后,就可以在此基础上集合 SQL映射文件信息 以及 MyBatis配置文件中的其他信息,创建SqlSessionFactory 实例
在MyBatis中,SqlSessionFactory 的实例需要使用SqlSessionFactoryBuilder 创建,而在集成环境中,则可以使用MyBatis-Spring 整合包中的 SqlSessionFactoryBean来代替.SqlSessionFactoryBean封装了使用SqlSessionFactoryBuilder 创建 SqlSessionFactory 的过程,我们可以在Spring中以配置文件的形式,通过SqlSessionFactoryBean 获得 SqlSessionFactory 实例.
代码如下:
<!--配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--引用数据源组件-->
<property name="dataSource" ref="dataSource"/>
<!--引用MyBatis配置文件中的配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--配置SQL映射文件信息-->
<!-- <property name="mapperLocations"> -->
<!--此处注意classpath 与 classpath* 的区别:
classpath:只会到你的class路径中查找找文件。
classpath*:不仅包含class路径,还包括jar文件中(class路径)进行查找。
注意: 用classpath*:需要遍历所有的classpath,所以加载速度是很慢的;
因此,在规划的时候,应该尽可能规划好资源文件所在的路径,尽量避免使用classpath*。-->
<!-- classpath*的使用:当项目中有多个classpath路径,并同时加载多个classpath路径下
(此种情况多数不会遇到)的文件,*就发挥了作用,如果不加*,则表示仅仅加载第一个classpath路径-->
<!-- <list>
<value>classpath*:cn/foo/dao/**/*.xml</value>
</list>
</property> -->
<property name="mapperLocations"
value="classpath*:cn/foo/mapper/*.xml" />
</bean>
上述代码中 通过 配置 id 为 sqlSessionFactory 的 Bean 即可获得 SqlSessionFactory实例
注意
逐个列出所有的SQL 映射文件比较烦琐, 在SqlSessionFactoryBean的配置中可以是使用mapperLocations属性扫描式加载SQL映射文件. 其中,
"classpath*:cn/smbms/dao/**/*.xml"
表示扫描 cn.smbms.dao 包及其任意层级子包中,任意名称的xml 类型文件.
此处注意** 与 ** 的区别:**classpath**``**classpath***
**classpath**
:只会到你的class路径中查找文件。**classpath***
:不仅包含class路径,还包括jar文件中(class路径)进行查找。注意: 用
**classpath***
:需要遍历所有的**classpath**
,所以加载速度是很慢的;**因此,在规划的时候,应该尽可能规划好资源文件所在的路径,尽量避免使用classpath。
的使用:当项目中有多个classpath路径,并同时加载多个classpath路径下(此种情况多数不会遇到)的文件,就发挥了作用,如果不加,则表示仅仅加载第一个classpath路径**classpath***``*``*
除了数据源和SQL映射信息,其他的MyBatis 配置信息也可以转移至Spring 配置文件中进行维护,只需要通过SqlSessionFactoryBean 的 对应属性进行赋值 即可. 具体可查阅官网 以及相关资料进行学习.
3.3 使用SqlSessionTemplate 实现数据库的操作
SqlSessionTemplate 类实现了MyBatis 的SqlSession接口,可以替换MyBatis中原有的SqlSession实现类提供数据库访问操作.
使用SqlSessionTemplate可以更好的与Spring 服务融合并简化部分流程化的工作,可以保证和当前Spring事务相关联,自动管理会话的生命周期,包括必要的关闭,提交和回滚操作.
**SqlSessionTemplate**
是 MyBatis-Spring 的核心。作为**SqlSession**
的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的**SqlSession**
。**SqlSessionTemplate**
是线程安全的,可以被多个 DAO 或映射器所共享使用。
当调用 SQL 方法时(包括由 **getMapper()**
方法返回的映射器中的方法),**SqlSessionTemplate**
将会保证使用的 **SqlSession**
与当前 Spring 的事务相关。此外,它管理 session 的生命周期,包含必要的关闭、提交或回滚操作。另外,它也负责将 MyBatis 的异常翻译成 Spring 中的 **DataAccessExceptions**
。
由于模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该用 **SqlSessionTemplate**
来替换 MyBatis 默认的 **DefaultSqlSession**
实现。避免在同一应用程序中的不同类之间混杂使用时 , 可能会引起数据一致性的问题。
可以使用
**SqlSessionFactory**
作为构造方法的参数来创建**SqlSessionTemplate**
对象。
配置SqlSessionTemplate 并在 UserMapper实现类中使用SqlSessionTemplate类,具体代码如下:
/**
* 定义 DAO 接口的实现类,实现UserMapper接口
*/
public class UserMapperImpl implements UserMapper {
private SqlSession sqlSession;
public void setSqlSession(SqlSession sqlSession) {
this.sqlSession = sqlSession;
}
@Override
public List<User> getUserList() {
return sqlSession.selectList("cn.foo.dao.user.UserMapper.getUserList");
}
}
Spring 配置文件中的关键代码如下:
<!--定义sqlSessionTemplate实例时,需要通过构造方法注入 sqlSessionFactory实例,
这里引用的是id 为 sqlSessionFactory的bean-->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!-- 配置DAO组件 并 注入 SqlSessionTemplate 实例 -->
<bean id="userMapper" class="cn.foo.dao.user.impl.UserMapperImpl">
<property name="sqlSessionTemplate" ref="sqlSessionTemplate"/>
</bean>
注意:
- 创建SqlSessionTemplate 实例时,需要通过其构造方法注入SqlSessionFactory实例.这里引用的是前文配置过的id为sqlSessionFactory的Bean
- 与MyBatis 中默认的SqlSession 实现不同,SqlSessionTemplate是线程安全的,可以以单例模式配置并被多个DAO对象共用,而且不必为每个DAO单独配置一个SqlSessionTemplate.
3.4 编写业务逻辑代码并测试
完成DAO组件的装配,接下来就可以在cn.foo.service.user 包中开发业务组件,并通过Spring装配.代码如下:
业务接口的关键代码:
public interface UserService {
/**
* 查询所用的用户信息
* @return
*/
List<User> findUserList();
}
业务接口实现类的关键代码:
public class UserServiceImpl implements UserService {
private UserMapper userMapper;
@Override
public List<User> findUserList() {
return userMapper.getUserList();
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
Spring 配置文件中的关键代码:
<!--配置业务Bean 并注入 DAO 实例-->
<bean id="userService"
class="cn.foo.service.user.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
完成所有组件的开发和装配之后,接下来就可以测试整合的效果了,测试方法中的关键代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-mybatis.xml")
public class TestUserService {
private final Logger logger = Logger.getLogger(this.getClass());
@Autowired
private UserService userService;
@Test
public void testGetUserList(){
List<User> userList = userService.findUserList();
userList.forEach(logger :: info);
}
}
Eclipse 运行测试代码,结果正确输出.但是,请注意 若是使用 IDEA 运行测试代码时,发现程序报错,报错信息如下:
###Caused by: org.springframework.core.NestedIOException: Failed to parse mapping resource: 'class path resource [cn/smbms/dao/user/UserMapper.xml]'; nested exception is java.io.FileNotFoundException: class path resource [cn/smbms/dao/UserMapper.xml] cannot be opened because it does not exist
这两句报错信息,很明显说的是 mapper.xml里的namespace报错 或者是方法名报错,但是仔细排查发现并未出现错误.错误原因究竟在哪儿呢?
这是因为 IDEA 在使用maven编译的时候,如果配置文件不是放在resources文件夹下就不会被执行编译,所以导致运行时找不到对应的xml映射文件。
解决方法:
- (1) 在main/resources目录下创建 mapper目录, 将对应的xml映射文件拖到mapper目录下,然后修改applicationContext-mybatis.xml文件中的 SqlSessionFactory的属性 mapperLocations,改为如下代码:
<!--配置SQL映射文件信息--> <!--<property name="mapperLocations"> <list> <value>classpath*:mapper/*Mapper.xml</value> </list> </property>--> <property name="mapperLocations" value="classpath*:mapper/*.xml" />
第一种方式的弊端是:破坏了项目的包结构.若项目代码已经很多了,而且很多人都在用,改变了整体结构就会变得很麻烦,所以建议采用第二种方式:
- (2) 在项目pom.xml文件中找到
<build></build>
标签,在其中添加以下几行,用于扫描xml文件<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources>
小结:
利用Spring框架和MyBatis-Spring整合资源提供的组件,能够以配置的方式得到数据源、SqlSessionFactoryBean、SqlSessionTemplate 等组件,并在此基础上完成DAO模块和业务模块的开发和装配,简化了开发过程且便于维护.
知识扩展:
针对SqlSessionTemplate 的使用,MyBatis-Spring 还提供了 SqlSessionDaoSupport 类 来简化 SqlSessionTemplate 的配置和获取. SqlSessionDaoSupport 用法如下:
修改 UserMapper的实现类,注视掉SqlSessionTemplate属性以及对应的setter方法,然后继承SqlSessionDaoSupport:
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper {
@Override
public List<User> getUserList() {
return this.getSqlSession().selectList("cn.foo.dao.user.UserMapper.getUserList");
}
}
Spring 配置文件中的关键代码如下:
<!--将之前id 为 sqlSessionFactory的bean注视掉,重新配置dao-->
<bean id="userMapper" class="cn.foo.dao.user.impl.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
SqlSessionDaoSupport类提供了setSqlSessionFactory()方法用来注入 SqlSessionFactory实例并创建SqlSessionTemplate实例,同时提供了getSqlSession()方法来返回创建好的SqlSessionTemplate实例.
这样,DAO实现类 只需要 继承自SqlSessionDaoSupport类,就可以通过调用getSqlSession()方法获得创建好的SqlSessionTemplate实例,无须额外定义 SqlSession 属性 和 setter方法.
而Spring的配置文件中 也无须再配置SqlSessionTemplate,只需要通过DAO对象的setSqlSessionFactory()方法为其注入SqlSessionFactory 即可,在一定程度上简化了DAO组件的开发.
如图:
4 . 注入映射器的实现
为了不需要一个个地注册所有的映射器。你可以让 MyBatis-Spring 对类路径进行扫描来发现它们。
有几种办法来发现映射器:
- 使用
<mybatis:scan/>
元素 - 使用
@MapperScan
注解 - 在经典 Spring XML 配置文件中注册一个
MapperScannerConfigurer
和 都在 MyBatis-Spring 1.2.0 中被引入。 需要你使用 Spring 3.1+。**<mybatis:scan/>**``**@MapperScan**``**@MapperScan**
4.1 使用MapperFacotoryBean注入映射器
与其在数据访问对象(DAO)中手工编写使用 SqlSessionDaoSupport 或 SqlSessionTemplate 的代码,还不如让 Mybatis-Spring 为你创建一个线程安全的映射器,这样你就可以直接注入到其它的 bean 中了
注册映射器的方法根据你的配置方法,即经典的 XML 配置或新的 3.0 以上版本的 Java 配置(也就是常说的 @Configuration),而有所不同。
注意 :
SQL 映射文件中须遵循以下命名规则:
- 映射文件中的命名空间 和 映射接口 的名称相同;
- 映射元素的id 和 映射器 接口的方法相同;
在之前案例的基础上,删除UserMapper的实现类UserMapperImpl,仅保留 UserMapper 接口 和 相关的SQL映射文件,在Spring 配置文件中,重新配置DAO组件,关键代码如下:
<!--配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--引用数据源组件-->
<property name="dataSource" ref="dataSource"/>
<!--引用MyBatis配置文件中的配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--配置SQL映射文件的信息-->
<!-- <property name="mapperLocations">
<list>
<value>classpath*:cn/foo/dao/**/*.xml</value>
</list>
</property>
<property name="mapperLocations"
value="classpath*:cn/foo/mapper/*.xml" />-->
</bean>
<!--配置DAO-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
<!--也可以使用构造注入的方式 注入接口的完全限定名-->
<!-- <constructor-arg value="cn.foo.dao.UserMapper"/>-->
<property name="mapperInterface" value="cn.foo.dao.user.UserMapper"/>
</bean>
<!--配置业务Bean 并注入 DAO 实例-->
<bean id="userService" class="cn.foo.service.user.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
业务组件的定义 和 配置以及测试代码与之前的相同.无须手工编码定义UserMapper的实现类. 通过配置MapperFactoryBean即可自动生成,减少了DAO模块的编码工作量.
注意:
- (1) 配置DAO组件 userMapper时,class属性不是某个实现类,而是
MapperFactoryBean
. - (2) 通过
mapperInterface
属性 指定映射器, 而且只能是接口类型,不能是某个实现类. - (3) MapperFactoryBean 是 SqlSessionDaoSupport 的子类,需要通过 setSqlSessionFactory()方法注入SqlSessionFactory实例 以创建 SqlSessionTemplate 实例.
- (4) 如果映射器对应的SQL文件与映射器的类路径相同,该映射文件可以自动被MapperFactoryBean解析.在此情况下,配置SqlSessionFactoryBean 时 可以不必指定 SQL 映射文件的位置.
如上述代码 将 SqlSessionFactoryBean中元素的属性 mapperLocations 注释掉之后,程序仍能正常运行;反之,若映射器与映射文件的类路径不同,则仍需在配置SqlSessionFactoryBean时 明确指定 映射文件的位置.
4.2 使用MapperScannerConfigurer注入映射器:
在Spring配置文件中 使用 MapperFactoryBean 对映射器做配置,简化了DAO模块的编码,不过如果映射器很多,相应的配置项应该也会很多.
为了简化配置工作量,MyBatis-Spring 中提供了MapperScannerConfigurer,它可以扫描指定包中的接口,并将它们直接注册为MapperFactoryBean.
MapperScannerConfigurer 的配置方法如下:
<!--配置sqlSessionFactoryBean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--引用数据源组件-->
<property name="dataSource" ref="dataSource"/>
<!--引用MyBatis配置文件中的配置-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--配置SQL映射文件的信息-->
<!-- <property name="mapperLocations">
<list>
<value>classpath*:cn/foo/dao/**/*.xml</value>
</list>
</property>-->
</bean>
<!--配置DAO 注意:这里的sqlSessionFatory是按照类型自动注入的 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--注意:这里的sqlSessionFatory是按照类型自动注入的 -->
<!--basePackage 属性 指定了扫描的基准包 MapperScannerConfigurer将递归扫描基准包下所有的接口
并将它们动态注册为MapperFactoryBean,如此即可批量生产映射器实现类-->
<property name="basePackage" value="cn.foo.dao"/>
</bean>
basePackage 属性 指定了扫描的基准包 MapperScannerConfigurer将递归扫描基准包(包括各层级子包)下所有的接口. 如果他们在SQL映射文件中定义过,则将它们动态注册为MapperFactoryBean,如此即可批量生产映射器实现类
注意 :
- (1) basePackage 属性中可以包含多个包名,多个包名之间可以使用逗号或分号隔开
- (2) MapperScannerConfigurer 会为所有由它创建的映射器开启自动装配。
也就是说:MapperScannerConfigurer创建的所有映射器实现都会(按照类型)被自动注入SqlSessionFactory 实例。**因此在上面代码中 配置DAO组件时,无须显式注入SqlSessionFactory实例。** - (3) 若环境中处于不同目的配置了多个SqlSessionFactory实例,自动装配将无法进行,此时应显式指定所依赖的SqlSessionFactory实例。
配置方式如下:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--basePackage 属性 指定了扫描的基准包 MapperScannerConfigurer将递归扫描基准包下所有的接口
并将它们动态注册为MapperFactoryBean,如此即可批量生产映射器实现类-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="cn.foo.dao"/>
</bean>
注意
- 此处使用的是sqlSessionFactoryBeanName属性而不是sqlSessionFactory属性,正如该属性名所表达的,这个属性关注的是Bean的名称,所以为其赋值使用的是value属性 而不是 ref属性.
通过配置MapperScannerConfigurer将映射器注册到Spring容器时,Spring会根据其接口名称为其命名,默认规则是首字母小写的非完全限定类名。
例如:UserMapper类型的组件会被默认命名为userMapper。按此命名规则,依然可以在Spring配置文件中按如下方式按业务组件注入映射器。
此处使用的是sqlSessionFactoryBeanName属性而不是sqlSessionFactory属性,正如该属性名所表达的,这个属性关注的是Bean的名称,所以为其赋值使用的是value属性 而不是 ref属性.
通过配置MapperScannerConfigurer将映射器注册到Spring容器时,Spring会根据其接口名称为其命名,默认规则是首字母小写的非完全限定类名.例如:UserMapper类型的组件会被默认命名为userMapper.按此命名规则,依然可以在Spring配置文件中按如下方式按业务组件注入映射器.
代码如下:
<!--配置业务Bean 并注入 DAO 实例-->
<bean id="userService" class="cn.foo.service.user.impl.UserServiceImpl">
<property name="userMapper" ref="userMapper"/>
</bean>
当然,更普遍的做法是,在MapperScannerConfigurer 自动完成映射器注册之后,使用@Autowired 或者 @Resource 注解实现对业务组件的依赖注入,以简化业务组件的配置. 使用注解对业务组件的定义和配置如下:
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> findUserList() {
return userMapper.getUserList();
}
public void setUserMapper(UserMapper userMapper) {
this.userMapper = userMapper;
}
}
Spring 配置文件中的关键代码如下:
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--开启注解扫描-->
<context:component-scan base-package="cn.foo.service"/>
<!--省略配置数据源-->
<!--省略 配置sqlSessionFactoryBean-->
<!--省略 配置DAO -->
</beans>
注意:Spring 配置文件中需要引入 context 命名空间
测试代码与之前相同
4.3 <mybatis:scan/>
<mybatis:scan/>
元素会发现映射器,它发现映射器的方法与 Spring 内建的 <context:component-scan/>
发现 bean 的方法非常类似。
示例如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://mybatis.org/schema/mybatis-spring
http://mybatis.org/schema/mybatis-spring.xsd">
<!-- 省略其他配置
从基包开始递归搜索接口,并将其注册为MapperFactoryBeans。
请注意,将只注册至少具有一个方法的接口;忽略具体类-->
<mybatis:scan base-package="cn.foo.mapper"/>
</beans>
base-package
属性允许你设置映射器接口文件的基础包。通过使用逗号或分号分隔,你可以设置多个包。并且会在你所指定的包中递归搜索映射器。
注意:
- 不需要为
<mybatis:scan/>
指定SqlSessionFactory
或SqlSessionTemplate
,这是因为它将使用能够被自动注入的MapperFactoryBean
。但如果你正在使用多个数据源(DataSource
),自动注入可能不适合你。在这种情况下,你可以使用factory-ref
或template-ref
属性指定你想使用的 bean 名称。 <mybatis:scan/>
支持基于标记接口或注解的过滤操作。在annotation
属性中,可以指定映射器应该具有的特定注解。而在marker-interface
属性中,可以指定映射器应该继承的父接口。当这两个属性都被设置的时候,被发现的映射器会满足这两个条件。默认情况下,这两个属性为空,因此在基础包中的所有接口都会被作为映射器被发现。
提示 <context:component-scan/>
无法发现并注册映射器。映射器的本质是接口,为了将它们注册到 Spring 中,发现映射器必须知道如何为找到的每个接口创建一个 MapperFactoryBean
。
4.4 知识扩展:
使用注解注入映射器:
1. 创建普通的Java类,类名为AppConfig,包名为cn.smbms.config,代码如下:
@Configuration
@ComponentScan("cn.foo.service")
@MapperScan("cn.foo.dao")
public class AppConfig {
@Bean
public DataSource dataSource(){
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/foo?useUnicode=true&characterEncoding=utf-8");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception{
SqlSessionFactoryBean sqlSessionFactoryBean =
new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
//为实体类设置别名
sqlSessionFactoryBean.setTypeAliasesPackage("cn.smbms.pojo");
return sqlSessionFactoryBean.getObject();
}
// @Bean
// public MapperScannerConfigurer mapperScannerConfigurer(){
// MapperScannerConfigurer mapperScannerConfigurer =
// new MapperScannerConfigurer();
// mapperScannerConfigurer.setBasePackage("cn.smbms.dao");
// return mapperScannerConfigurer;
// }
}
@MapperScan
解释:这个注解具有与之前见过的 <mybatis:scan/>
元素一样的工作方式。它也可以通过 markerInterface
和 annotationClass
属性设置标记接口或注解类
2. 创建测试类代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes= AppConfig.class)
public class TestAppConfigUser {
private final Logger logger = Logger.getLogger(this.getClass());
@Autowired
private UserService userService;
@Test
public void testGetUserList(){
List<User> userList = userService.findUserList();
userList.forEach(logger :: info);
}
}
其他的不做改动,运行测试代码,控制台正常输出.
小结:
MapperScannerConfigurer 与 @Autowired 注解 或 @Resource 注解配置使用,自动创建映射器并注入给业务组件,最大限度的减少DAO组件与业务组件的编码和配置工作.而在实际开发中也是这种方式使用的更多,而基于全注解的配置用的很少,因此了解即可.
5 . 为业务层添加声明式事务
业务层的的职能不仅仅是调用DAO这么简单,事务处理是任何企业级应用开发中不能回避的一个重要问题. 以往我们通过在业务方法中硬编码的方式进行事务控制,这样做的弊端显而易见:事务代码分散在业务方法中难以重用,需要调整时工作量也比较大;复杂事务的编码难度较高,增加了开发难度等.
Spring 提供了声明式事务处理机制,它基于AOP实现,无须编写任何事务管理代码,所有的工作全在配置文件中完成.这意味着与业务代码完全分离,配置即可用,降低了开发和维护的难度.
5.1 配置声明式事务:
这里以添加用户的功能为例,介绍如何实现声明式事务处理.
- 首先在UserMapper 和 UserMapper.xml中添加相关的方法和SQL映射,代码如下:
- UserMapper.java 中 添加的关键代码:
int add(User user);
- UserMapper.xml 中 添加的关键代码:
<!--新增用户信息--> <insert id="add" parameterType="user"> insert into foo_user <trim prefix="(" suffix=")" suffixOverrides=","> <if test="userCode !=null and userCode != ''"> userCode, </if> <if test="userName !=null and userName != ''"> userName, </if> <if test="userPassword !=null and userPassword != ''"> userPassword, </if> <if test="gender !=null"> gender, </if> <if test="birthday !=null"> birthday, </if> <if test="phone !=null and phone!='' "> phone, </if> <if test="address !=null and address!=''"> address, </if> <if test="userRole !=null"> userRole, </if> <if test="createdBy !=null"> createdBy, </if> <if test="creationDate !=null"> creationDate </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="userCode !=null and userCode != ''"> #{userCode}, </if> <if test="userName !=null and userName != ''"> #{userName}, </if> <if test="userPassword !=null and userPassword != ''"> #{userPassword}, </if> <if test="gender !=null"> #{gender}, </if> <if test="birthday !=null"> #{birthday}, </if> <if test="phone !=null and phone!='' "> #{phone}, </if> <if test="address !=null and address!=''"> #{address}, </if> <if test="userRole !=null"> #{userRole}, </if> <if test="createdBy !=null"> #{createdBy}, </if> <if test="creationDate !=null"> #{creationDate} </if> </trim> </insert>
由于使用了MapperScannerConfigurer 动态创建映射器实现,没有自定义的接口实现类,也就没有相关代码需要修改.
- 在业务组件中添加的相应的业务方法:
- 业务接口中添加相关的业务方法,代码如下:
boolean addNewUser(User user);
- 业务实现类中的方法实现代码:
@Override
public boolean addNewUser(User user) {
boolean result = userMapper.add(user)>0;
//int i = 3/0;//模拟异常发生时 事务是否回滚
return result;
}
注意:
业务类中没有事务控制的相关代码,所以将 int i = 3/0 去掉其注释时,运行测试类发现事务并没有回滚.
接下来为业务方法配置事务切面,这里需要用到 tx 和 aop 两个命名空间下的标签,所以首先在Spring配置文件中导入这两个命名空间.代码如下:
<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 省略具体配置 -->
</beans>
接下来需要配置一个事务管理组件,它提供了对事务处理的全面支持和统一管理,在切面中相当于增强处理的角色.这里使用Spring 提供的事务管理器类 DataSourceTransactionManager
, 其配置方式如下:
<!--省略数据源 SqlSessionFactoryBean DAO 以及 业务Bean的配置-->
<!--定义事务管理器-->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- <property name="dataSource" ref="dataSource"/>-->
<constructor-arg ref="dataSource"/>
</bean>
注意:
配置**DataSourceTransactionManage**
时, 要为其注入事先定义好的数据源组件,可参考Spring的官网:**https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-first-example**
,如图所示:
与之前接触过的增强处理不同,事务管理器还可以进一步配置,以便更好的适应不同的业务方法对于事务的不同要求.
可以通过<tx:advice>
标签配置事务增强,设定事务属性,为不同的业务方法指定具体的事务规则.其代码片段如下:
<!--通过<tx:advice>标签为指定的事务管理器 设置事务属性
默认使用的事务管理器的bean的名字是:transactionManager,
若定义的事务管理器的Bean的id是:transactionManager,则可以不用指定
transaction-manager属性-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--定义属性,声明事务规则-->
<tx:attributes>
<tx:method name="find*" propagation="SUPPORTS"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
在<tx:advice>
标签内可以设置id属性 和 transaction-manager属性.其中 transaction-manager 属性 引用一个事务管理器Bean.
详细配置可参考官网:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-first-example 。 如图所示:
注意:
transaction-manager 属性的默认值是 transactionManager,也就是说,如果定义的事务管理器Bean的名称是transactionManager,则可以不指定该属性值
除了这两个属性外,还可以通过**<tx:attributes>**
子标签定制事务属性.事务属性通过**<tx:method>**
标签进行设置.Spring支持对不同的方法设置事务属性,所以可以为一个**<tx:attributes>**
设置多个**<tx:method>**
.
<tx:method>
标签中中的name属性是必需的,用于指定匹配的方法.这里需要对方法名进行约定,可以使用通配符(*
).其他属性均为可选,用于指定具体的事务规则,这些属性解释如下:
propagation : 事务传播机制.该属性可选的值 有 如下几种:
- REQUIRED:默认值:表示如果存在事务,则支持当前事务,如果当前没有事务,则开启一个新的事务
- PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
- REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
- MANDATORY(强制的):支持当前事务,如果当前没有事务,就抛出异常
- NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
- NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
经验:
- REQUIRED : 能过满足大多数的事务需求,可以作为首选的事务传播行为
isolation : 事务隔离级别.即当前事务和其他事务的隔离程度,在并发事务处理的情况下,需要考虑它的设置.该属性可选的值有如下几种:
- DEFAULT(一般情况下使用这种配置即可):这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别
- READ_COMMITTED:读已提交的数据(会出现不可重复读,幻读)大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统
- READ_UNCOMMITTED:保证了读取过程中不会读取到非法数据。
- REPEATABLE_READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失
- SERIALIZABLE:最严格的级别,事务串行执行,资源消耗最大;
timeout:事务超时时间,允许事务运行的最长时间,以秒为单位。默认值为-1,表示不超时
read-only:事务是否为只读,默认值为false
rollback-for:设定能够触发回滚的异常类型,Spring默认只在抛出RuntimeException时才标识事务回滚,可以通过全限定类名指定需要回滚事务的异常,多个类名用逗号隔开
no-rollback-for:设定不触发回滚的异常类型,Spring默认 checked Exception 不会触发事务回滚,可以通过全限定类名指定不需回滚事务的异常,多个类名用英文逗号隔开
关于事务规则更加详细的说明,请参考官网:https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#transaction-declarative-txadvice-settings
;
设置完事务规则,最后还要定义切面,将事务规则应用到指定方法上.代码如下:
<!--定义切面-->
<aop:config>
<!--定义切入点-->
<aop:pointcut id="pointcut" expression="execution(* cn.foo.service.user.*.*(..))"/>
<!--将事务增强 与 切入点结合-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
</aop:config>
注意:
aop:advisor 和 advice-ref 属性引用的是通过
**<tx:advice>**
标签设定了事务属性的组件.
至此 Spring的声明式事务就配置完成了,最后再总结一下配置的步骤:
- (1) 导入 tx 和 aop 命名空间
- (2) 定义事务管理器Bean,并为其注入数据源Bean
- (3) 通过
**<tx:advice>**
配置事务增强,绑定事务管理器并针对不同方法定义事务规则 - (4) 配置切面,将事务增强 与 方法切入点 组合
测试方法的关键代码如下:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-mybatis.xml")
public class TestUserService {
private final Logger logger = Logger.getLogger(this.getClass());
@Autowired
private UserService userService;
@Test
public void testAddNewUser() throws Exception{
User user = new User();
user.setUserCode("tom");
user.setUserName("Tom");
user.setUserPassword("123");
user.setAddress("New York");
user.setGender(1);
user.setBirthday(new SimpleDateFormat("yyyy-MM-dd").parse("1990-09-09"));
user.setPhone("13800000000");
user.setCreatedBy(1);
user.setCreationDate(new Date());
boolean flag = userService.addNewUser(user);
logger.info("是否添加成功==>"+flag);
}
}
5.2 使用注解实现声明式事务处理
Spring 还支持使用注解配置声明式事务,所使用的注解是 @Transactional
首先 仍然需要在Spring配置文件中配置事务管理类,并添加对注解配置的事务的支持,代码如下:
<!--定义事务管理器-->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- <property name="dataSource" ref="dataSource"/>-->
<constructor-arg ref="dataSource"/>
</bean>
<!--启用事务注解 属性 transaction-manager的默认值是 transactionManager
若指定事务管理器的Bean的id 为 transactionManager,此属性可以不用指定 -->
<tx:annotation-driven transaction-manager="transactionManager" />
经过如上配置,程序边支持使用@Transactional 注解来配置事务了,代码如下:
@Transactional
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public List<User> findUserList() {
return userMapper.getUserList();
}
@Override
public boolean addNewUser(User user) {
boolean result = userMapper.add(user)>0;
int i = 3/0;//测试程序出现RuntimeException时,事务是否回滚
return result;
}
}
在业务实现类上添加@Transactional 注解即为该类的所有业务方法统一添加事务处理,如果某一业务方法需要采用不同的事务规则,可以在该业务方法上添加@Transactional注解单独进行设置.
@Transactional 注解也可以设置事务属性的值,默认的@Transactional 设置如下:
- 事务传播设置是 PROPAGATION_REQUIRED
- 事务隔离级别是 ISOLATION_DEFAULT
- 事务是读/写
- 事务超时默认是依赖于事务系统的,或者事务超时没有被支持
- 任何 RuntimeException 将触发事务回滚,但是任何checked Exception 将不触发事务回滚.
这些默认的设置当然也是可以改变的.@Transactional注解的各属性如表:
属性 | 类型 | 说明 |
---|---|---|
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的子类 | 一组异常类名,遇到时必须不回滚 |
5.3 知识扩展:
全部使用注解实现编程式事务管理:
修改配置类的代码:
@Configuration
@ComponentScan(basePackages = "cn.smbms.service")
@MapperScan(basePackages = "cn.smbms.mapper")
@EnableTransactionManagement//启用事务管理注解
public class AppConfig {
@Bean
public DataSource dataSource(){
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/smbms?useUnicode=true&characterEncoding=utf-8");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean =
new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource());
sqlSessionFactoryBean.setTypeAliasesPackage("cn.smbms.pojo");
return sqlSessionFactoryBean.getObject();
}
@Bean
public TransactionManager transactionManager(){
return new DataSourceTransactionManager(dataSource());
}
}
业务类的注解与声明式事务中的注解一致