mybatis 动态SQL以及和spring的集成
3.6 动态SQL
有时候,静态的 SQL 语句并不能满足应用程序的需求。我们可以根据一些条件,来动态地构建 SQL 语句。
例如,在 Web 应用程序中,有可能有一些搜索界面,需要输入一个或多个选项,然后根据这些已选择的条件去执行检 索操作。在实现这种类型的搜索功能,我们可能需要根据这些条件 来构建动态的 SQL 语句。如果用户提供了任何输入条 件,我们需要将那个条件 添加到SQL语句的WHERE子句中。
MyBatis 通过使用if,choose,where,foreach,trim元素提供了对构造动态 SQL 语句的高级别支持。
3.6.1 if条件
if元素被用来有条件地嵌入 SQL 片段,如果测试条件被赋值为 true,则相应地 SQL 片段将会被添加到 SQL 语 句中。
假定我们有一个课程搜索界面,设置了 讲师(Tutor)下拉列表框,课程名称(CourseName)文本输入框,开始 时间(StartDate)输入框,结束时间(EndDate)输入框,作为搜索条件。假定课讲师下拉列表是必须选的,其他的 都是可选的。
当用户点击 搜索 按钮时,我们需要显示符合以下条件的成列表:
- 特定讲师的课程
- 课程名 包含输入的课程名称关键字的课程;如果课程名称输入为空,则取所有课程
- 在开始时间和结束时间段内的课程
我们可以对应的映射语句,如下所示:
<resultMap type="Course" id="CourseResult">
<id property="courseId" column="course_id" />
<result property="name" column="name" />
<result property="description" column="" />
<result property="startDate" column="start_date" />
<result property="endDate" column="end_date" />
</resultMap>
<select id="searchCourses" parameterType="hashMap" resultMap="CourseResult">
select * from courses
where tutor_id=#{tutorId}
<if test="courseName!=null">
and name like #{courseName}
</if>
<if test="startDate!=null">
and start_date <![CDATA[>=]]> #{startDate}
</if>
<if test="endDate!=null">
and end_date <![CDATA[<=]]> endDate
</if>
</select>
public interface CourseMapper
{
List<Course> searchCourses(Map<String, Object> map);
}
public void searchCourses()
{
Map<String, Object> map = new HashMap<String, Object>();
map.put("tutorId", 1);
map.put("courseName", "%java%");
map.put("startDate", new Date());
CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
List<Course> courses = mapper.searchCourses(map);
for (Course course : courses)
{
System.out.println(course);
}
}
此处将生成查询语句 SELECT * FROM COURSES WHERE TUTOR_ID= ? AND NAME like ? AND START_DATE >= ?。 准备根据给定条件的动态 SQL 查询将会派上用场。
MyBatis 是使用 ONGL(Object Graph Navigation Language)表达式来构建动态 SQL 语句
3.6.2 choose,when 和 otherwise 条件
有时候,查询功能是以查询类别为基础的。首先,用户需要选择是否希望通过选择 讲师,课程名称,开始时间, 或结束时间作为查询条件类别来进行查询,然后根据选择的查询类别,输入相应的参数。在这样的情景中,我们需要 只使用其中一种查询类别。
MyBatis 提供了choose元素支持此类型的 SQL 预处理.
现在让我们书写一个适用此情景的 SQL 映射语句。如果没有选择查询类别,则查询开始时间在今天之后的课程
<select id="searchCourses2" parameterType="hashMap" resultMap="CourseResult">
select * from courses
<choose>
<when test="searchBy=='Tutor'">
where tutor_id=#{tutorId}
</when>
<when test="searchBy=='CourseName'">
where name like #{courseName}
</when>
<otherwise>
where start_date <![CDATA[>=]]> #{startDate}
</otherwise>
</choose>
</select>
MyBatis 计算choose测试条件的值,且使用第一个值为 TRUE 的子句。如果没有条件为 true,则使用<otherwise> 内的子句。
3.6.2 where条件
有时候,所有的查询条件(criteria)应该是可选的。在需要使用至少一种查询条件的情况下,我们应该使用 WHERE 子句。并且, 如果有多个条件,我们需要在条件中添加AND或OR。MyBatis提供了where元素支持这种类型的动 态 SQL 语句。
在我们查询课程界面,我们假设所有的查询条件是可选的。进而,当需要提供一个或多个查询条件时,应该改使用 WHERE 子句。
<select id="searchCourses3" parameterType="hashMap" resultMap="CourseResult">
select * from courses
<where>
<if test="tutorId!=null">
tutor_id=#{tutorId}
</if>
<if test="courseName!=null">
and name like #{courseName}
</if>
<if test="startDate!=null">
and start_date <![CDATA[>=]]> #{startDate}
</if>
<if test="endDate!=null">
and end_date <![CDATA[<=]]> #{endDate}
</if>
</where>
</select>
where元素只有在其内部标签有返回内容时才会在动态语句上插入 WHERE 条件语句。并且,如果 WHERE 子句以 AND 或者 OR 打头,则打头的 AND 或 OR 将会被移除。
如果 tutor_id 参数值为 null,并且 courseName 参数值不为 null,则where标签会将 AND name like #{courseName} 中的 AND 移除掉,生成的 SQL WHERE 子句为:where name like #{courseName}。
3.6.4 trim条件
trim元素和where元素类似,但是<trim>提供了在添加前缀/后缀 或者 移除前缀/后缀方面提供更大的灵活 性。
<select id="searchCourses" parameterType="hashMap" resultMap="CourseResult">
select * from courses
<trim prefix="where" prefixOverrides="AND | OR">
<if test="tutorId!=null">
tutor_id=#{tutorId}
</if>
<if test="courseName!=null">
and name like #{courseName}
</if>
</trim>
</select>
这里如果任意一个if条件为 true,trim元素会插入 WHERE,并且移除紧跟 WHERE 后面的 AND 或 OR
3.6.5 foreach 循环
另外一个强大的动态 SQL 语句构造标签即是foreach。它可以迭代遍历一个数组或者列表,构造 AND/OR 条件或 一个 IN 子句。
假设我们想找到 tutor_id 为 1,3,6 的讲师所教授的课程,我们可以传递一个 tutor_id 组成的列表给映射语 句,然后通过<foreach>遍历此列表构造动态 SQL。
<select id="searchCoursesByTutors1" parameterType="map" resultMap="CourseResult">
select * from courses
<if test="tutorIds!=null">
<where>
<foreach collection="tutorIds" item="tutorId">
or tutor_id=#{tutorId}
</foreach>
</where>
</if>
</select>
public void searchCoursesByTutors() {
Map<String, Object> map = new HashMap<>();
List<Integer> tutorIds = new ArrayList<>();
tutorIds.add(1);
tutorIds.add(2);
tutorIds.add(6);
map.put("tutorIds", tutorIds);
TutorService tutorService = new TutorService();
List<Course> courses = tutorService.searchCoursesByTutors(map);
System.out.println("++++");
for (Course course : courses) {
System.out.println("-----");
System.out.println(course);
}
}
看一下怎样使用<foreach>生成 IN 子句:
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
select * from courses
<if test="tutorIds!=null">
<where>
tutor_id in
<foreach collection="tutorIds" item="tutorId" open="(" separator="," close=")">
#{tutorId}
</foreach>
</where>
</if>
</select>
3.6.6 set 条件:
set元素和where元素类似,如果其内部条件判断有任何内容返回时,他会插入 SET SQL 片段。
<update id="updateTutor" parameterType="Tutor">
update tutors
<set>
<if test="name!=null">name=#{name},</if>
<if test="email!=null">email=#{email},</if>
</set>
where tutor_id=#{tutorId}
</update>
这里,如果if条件返回了任何文本内容,set将会插入set关键字和其文本内容,并且会剔除将末尾的 “,”。 在上述的例子中,如果 email!=null,set将会让会移除email=#{email}后的逗号“,”,生成 set email=#{email} 。
3.7 mybatis 其它功能
除了简化数据库编程外,MyBatis 还提供了各种功能,这些对实现一些常用任务非常有用,比如按页加载表数据,存取 CLOB/BLOB 类型的数据,处理枚举类型值,等等。让我们来看看其中一些特性吧。
3.7.1 处理枚举类型
MyBatis支持开箱方式持久化enum 类型属性。假设STUDENTS表中有一列gender(性别)类型为varchar,存 储”MALE”或者“FEMALE”两种值。并且,Student 对象有一个 enum 类型的 gender 属性,如下所示:
public enum Gender
{
FEMALE,
MALE
}
默认情况下,MyBatis 使用 EnumTypeHandler 来处理 enum 类型的 Java 属性,并且将其存储为 enum 值的名称。你 不需要为此做任何额外的配置。你可以可以向使用基本数据类型属性一样使用 enum 类型属性,代码如下:
public class Student
{
private Integer id;
private String name;
private String email;
private PhoneNumber phone;
private Address address;
private Gender gender;
//setters and getters
}
<insert id="insertStudent" parameterType="Student"
useGeneratedKeys="true" keyProperty="id">
insert into students(name,email,addr_id, phone,gender)
values(#{name},#{email},#{address.addrId},#{phone},#{gender})
</insert>
当你执行 insertStudent 语句的时候,MyBatis 会取 Gender 枚举(FEMALE/MALE)的名称,然后将其存储到 GENDER 列中。
如果你希望存储原 enum 的顺序位置,而不是 enum 名,你需要明确地配置它。
如果你想存储 FEMALE 为 0,MALE 为 1 到 gender 列中,你需要在 mybatis-config.xml 文件中配置 EnumOrdinalTypeHandler:
<typeHandler
handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="com.mrq.domain.Gender"/>
使用顺序位置为值存储到数据库时要当心。顺序值是根据 enum 中的声明顺序赋值 的。如果你改变了 Gender enum 的声明顺序,则数据库存储的数据和此顺序值就 不匹配了。
3.7.2 传入多个输入参数
MyBatis 中的映射语句有一个 parameterType 属性来制定输入参数的类型。如果我们想给映射语句传入多个参数的 话,我们可以将所有的输入参数放到 HashMap 中,将 HashMap 传递给映射语句。
MyBatis 还提供了另外一种传递多个输入参数给映射语句的方法。假设我们想通过给定的name和email信息查找 学生信息,定义查询接口如下:
Public interface StudentMapper
{
List<Student> findAllStudentsByNameEmail(String name, String email);
}
MyBatis 支持 将多个输入参数传递给映射语句,并以#{param}的语法形式引用它们:
<select id="findAllStudentsByNameEmail" resultMap="StudentResult">
select stud_id, name,email, phone from Students
where name=#{param1} and email=#{param2}
</select>
这里#{param1}引用第一个参数 name,而#{param2}引用了第二个参数 email。
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.
class);
studentMapper.findAllStudentsByNameEmail(name, email);
3.7.2 多行结果集映射成 Map
如果你有一个映射语句返回多行记录,并且你想以 HashMap 的形式存储记录的值,使用记录列名作为 key 值,而记 录对应值或为 value 值。我们可以使用 sqlSession.selectMap(),如下所示:
<select id=" findAllStudents" resultMap="StudentResult">
select * from Students
</select>
Map<Integer, Student> studentMap =
sqlSession.selectMap("com.mrq.mappers.StudentMapper.findAllStudents", "studId");
这里 studentMap 将会将 studId 作为 key 值,而 Student 对象作为 value 值。
3.7.4 使用 RowBounds 对结果集进行分页
有时候,我们会需要跟海量的数据打交道,比如一个有数百万条数据级别的表。由于计算机内存的现实我们不可能一 次性加载这么多数据,我们可以获取到数据的一部分。特别是在 Web 应用程序中,分页机制被用来以一页一页的形式展 示海量的数据。
MyBatis 可以使用 RowBounds 逐页加载表数据。RowBounds 对象可以使用 offset 和 limit 参数来构建。参数 offset 表示开始位置,而 limit 表示要取的记录的数目。
假设如果你想每页加载并显示 25 条学生的记录,你可以使用如下的代码:
<select id="findAllStudents" resultMap="StudentResult">
select * from Students
</select>
然后,你可以加载如下加载第一页数据(前 25 条):
int offset =0 , limit =25;
RowBounds rowBounds = new RowBounds(offset, limit);
List<Student> = studentMapper.getStudents(rowBounds);
若要展示第二页,使用 offset=25,limit=25;第三页,则为 offset=50,limit=25。
3.7.5 使用 ResultSetHandler 自定义结果集 ResultSet 处理
MyBatis 在将查询结果集映射到 JavaBean 方面提供了很大的选择性。但是,有时候我们会遇到由于特定的目的,需 要我们自己处理 SQL 查询结果的情况。MyBatis 提供了 ResultHandler 插件形式允许我们以任何自己喜欢的方式处理 结果集 ResultSet。
假设我们想从学生的 stud_id 被用作 key,而 name 被用作 value 的 HashMap 中获取到 student 信息。
对于 sqlSession.select()方法,我们可以传递给它一个 ResultHandler 的实现,它会被调用来处理 ResultSet 的每一条记录。
看一下怎么使用 ResultHandler 来处理结果集 ResultSet,并返回自定义化的结果。
public Map<Integer, String> getTutorIdNameMap() {
final Map<Integer, String> map = new HashMap<>();
SqlSession sqlSession = MyBatisSqlSessionFactory.openSession();
try {
sqlSession.select("com.mrq.mappers.TutorMapper.findAllTutors",
new ResultHandler<Tutor>() {
@Override
public void handleResult(ResultContext<? extends Tutor> context) {
// TODO Auto-generated method stub
Tutor tutor = context.getResultObject();
map.put(tutor.getTutorId(),tutor.getName());
}
});
} finally {
//sqlSession.close();
MyBatisSqlSessionFactory.closeSqlSession();
}
return map;
}
在上述的代码中,我们提供了匿名内部 ResultHandler 实现类,在 handleResult()方法中,我们使用 context.getResultObject()获取当前的 result 对象,即 Tutor对象,因为我们定义了 findAllTutors 映射语 句的 resultMap=”tutorResult“。对查询返回的每一行都会调用 handleResult()方法,并且我们从 findAllTutors 对象 中取出 tutorId 和 name,将其放到 map 中。
3.7.6 缓存
将从数据库中加载的数据缓存到内存中,是很多应用程序为了提高性能而采取的一贯做法。MyBatis 对通过映射的 SELECT 语句加载的查询结果提供了内建的缓存支持。默认情况下,启用一级缓存;即,如果你使用同一个 SqlSession 接口对象调用了相同的 SELECT 语句,则直接会从缓存中返回结果,而不是再查询一次数据库。
我们可以在 SQL 映射器 XML 配置文件中使用cache 元素添加全局二级缓存。 当你加入了cache 元素,将会出现以下情况:
- 所有的在映射语句文件定义的select语句的查询结果都会被缓存
- 所有的在映射语句文件定义的insert,update 和delete语句将会刷新缓存
- 缓存根据最近最少被使用(Least Recently Used,LRU)算法管理
- 缓存不会被任何形式的基于时间表的刷新(没有刷新时间间隔),即不支持定时刷新机制
- 缓存将存储1024个 查询方法返回的列表或者对象的引用
- 缓存会被当作一个读/写缓存。这是指检索出的对象不会被共享,并且可以被调用者安全地修改,不会其他潜 在的调用者或者线程的潜在修改干扰。(即,缓存是线程安全的)
可以通过复写默认属性来自定义缓存的行为,如下所示:
<cache eviction="FIFO" flushInterval="60000" size="512"
readOnly="true"/>
以下是对上述属性的描述:
- eviction:此处定义缓存的移除机制。默认值是 LRU,其可能的值有:LRU(least recently used,最近最少使用),FIFO(first in first out,先进先出),SOFT(soft reference,软引用),WEAK(weakreference,弱引用)。
- flushInterval:定义缓存刷新间隔,以毫秒计。默认情况下不设置。所以不使用刷新间隔,缓存 cache 只有调用语句的时候刷新。
- size:此表示缓存 cache 中能容纳的最大元素数。默认值是 1024,你可以设置成任意的正整数。
- readOnly:一个只读的缓存 cache 会对所有的调用者返回被缓存对象的同一个实例(实际返回的是被返回对象的一份引用)。一个读/写缓存 cache 将会返回被返回对象的一分拷贝(通过序列化)。默认情况下设置为 false。可能的值有 false 和 true。
一个缓存的配置和缓存实例被绑定到映射器配置文件所在的名空间(namespace)上,所以在相同名空间内的所有语
句被绑定到一个 cache 中。
认的映射语句的 cache 配置如下:
<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>
可以为任意特定的映射语句复写默认的 cache 行为;例如,对一个 select 语句不使用缓存,可以设置 useCache=“false”。
除了内建的缓存支持,MyBatis 也提供了与第三方缓存类库如 Ehcache,OSCache,Hazelcast 的集成支持。你可 以在MyBatis官方网站https://code.google.com/p/mybatis/wiki/Caches 上找到关于继承第三方缓存类库的更 多信息。
四. mybatis与spring集成
MyBatis-Spring 是 MyBatis 框架的子模块,用来提供与当前流行的依赖注入框架 Spring 的无缝集成。
Spring 框架是一个基于依赖注入(Dependency Injection)和面向切面编程(Aspect Oriented Programming,AOP)的 Java 框架,鼓励使用基于 POJO 的编程模型。另外,Spring 提供了声明式和编程式的事务管理 能力,可以很大程度上简化应用程序的数据访问层(data access layer)的实现。在章节中,我们将看到在基于 Spring 的应用程序中使用 MyBatis 并且使用 Spring 的基于注解的事务管理机制。
4.1 在spring应用程序中配置mybatis
4.1.1 安装
使用 Maven 构建工具,你可以配置 MyBatis 的 spring 依赖如下:
pom.xml:
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.version>4.3.0.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<!--mybatis-spring适配器 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.0</version>
</dependency>
<!--Spring框架核心库 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- aspectJ AOP 织入器 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<!--Spring java数据库访问包,在本例中主要用于提供数据源 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
4.1.2 配置 MyBatis Beans
spring-mvc.xml
<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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:annotation-config></context:annotation-config>
<context:property-placeholder location="classpath:application.properties" />
<context:component-scan base-package="com.mrq"></context:component-scan>
<!-- ========================================配置数据源========================================= -->
<!--定义一个jdbc数据源,创建一个驱动管理数据源的bean -->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="acquireIncrement" value="5"></property>
<property name="initialPoolSize" value="10"></property>
<property name="minPoolSize" value="5"></property>
<property name="maxPoolSize" value="20"></property>
</bean>
<!-- ========================================分隔线========================================= -->
<!-- ========================================针对myBatis的配置项============================== -->
<!-- 配置sqlSessionFactory -->
<!--创建一个sql会话工厂bean,指定数据源 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 实例化sqlSessionFactory时需要使用上述配置好的数据源以及SQL映射文件 -->
<property name="dataSource" ref="dataSource" />
<!-- 自动扫描com.mrq.mappers/目录下的所有SQL映射的xml文件, 省掉Configuration.xml里的手工配置
value="classpath:com/mrq/mappers/*.xml"指的是classpath(类路径)下com.mrq.mappers包中的所有xml文件
TutorMapper.xml位于com.mrq.mappers包下,这样TutorMapper.xml就可以被自动扫描
-->
<property name="mapperLocations" value="classpath:com/mrq/mappers/*.xml" />
</bean>
<!-- 配置扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 扫描com.mrq.mappers这个包以及它的子包下的所有映射接口类 -->
<property name="basePackage" value="com.mrq.mappers" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
<!-- ========================================分隔线========================================= -->
<!--5 声明式事务管理 -->
<!--定义事物管理器,由spring管理事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--支持注解驱动的事务管理,指定事务管理器 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--6 容器自动扫描IOC组件 -->
<context:component-scan base-package="com.mrq"></context:component-scan>
<!--7 aspectj支持自动代理实现AOP功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
使用上述的 bean 定义,Spring 会使用如下配置属性创建一个 SqlSessionFactory 对象:
- dataSource:它引用了 dataSource bean
- typeAliases:它指定了一系列的完全限定名的类名列表,用逗号隔开,这些别名将通过默认的别名规则创建(将首字母小写的非无完全限定类名作为别名)。
- typeAliasesPackage:它指定了一系列包名列表,用逗号隔开,包内含有需要创建别名的JavaBeans。
- typeHandlers:它指定了一系列的类型处理器类的完全限定名的类名列表,用逗号隔开。
- typeHandlersPackage: 它指定了一系列包名列表,用逗号隔开,包内含有需要被注册的类型处理器类。
- mapperLocations:它指定了 SQL 映射器 Mapper XML 配置文件的位置
- configLocation:它指定了 MyBatisSqlSessionFactory 配置文件所在的位置。
- MapperScannerConfigurer 来扫描包 (package)中的映射器 Mapper 接口,并自动地注册。
- 使用 Spring 的基于注解的事务处理机制来避免书写上述的每个方法中控制事务的冗余代码。为了能使用 Spring 的事务管理功能,我们需要在 Spring 应用上下文中配置 TransactionManager bean 实体对 象:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
在 Spring 中使用基于注解的事务管理特性,如下:
<tx:annotation-driven transaction-manager="transactionManager"/>
现在你可以在 Spring service bean 上使用@Transactional 注解,表示在此 service 中的每一个方法都应该在 一个事务中运行。如果方法成功运行完毕,Spring 会提交操作。如果有运行期异常发生,则会执行回滚操作。另外, Spring 会将 MyBatis 的异常转换成合适的 DataAccessExceptions,这样会为特定错误上提供额外的信息。
package com.mrq.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.mrq.dao.TutorDao;
import com.mrq.domain.Tutor;
@Service
public class TutorService {
private TutorDao tutorDao;
@Autowired
public void setTutorDao(TutorDao tutorDao) {
this.tutorDao = tutorDao;
}
public Tutor findTutorById(int tutorId) {
return tutorDao.findTutorById(1);
}
@Transactional
public int insertTutor(Tutor tutor) {
return tutorDao.insertTutor(tutor);
}
}
写一个独立的测试客户端来测试 TutorService,如下:
package com.mrq.main;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mrq.domain.Tutor;
import com.mrq.service.TutorService;
public class TestTutorService {
static TutorService tutorService;
@BeforeClass
public static void before() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring-mvc.xml");
tutorService = (TutorService) ctx.getBean("tutorService");
}
@Test
public void testFindTutor() {
Tutor tutor = tutorService.findTutorById(1);
System.out.println(tutor+"---====");
}
@Test
public void insertTutor() {
Tutor tutor = new Tutor();
tutor.setEmail("Piter@163.com");
tutor.setName("Piter");
int count = tutorService.insertTutor(tutor);
System.out.println("insert-> "+count);
}
}
domain:
public class Tutor {
private Integer tutorId;
private String name;
private String email;
private String phone;
//setter and getter
}
dao:
package com.mrq.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.mrq.domain.Tutor;
import com.mrq.mappers.TutorMapper;
@Repository
public class TutorDao {
private TutorMapper tutorMapper;
@Autowired
public void setTutorMapper(TutorMapper tutorMapper) {
this.tutorMapper = tutorMapper;
}
public Tutor findTutorById(int tutorId) {
return tutorMapper.findTutorById(1);
}
public int insertTutor(Tutor tutor) {
return tutorMapper.insertTutor(tutor);
}
}
service:
package com.mrq.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.mrq.dao.TutorDao;
import com.mrq.domain.Tutor;
@Service
public class TutorService {
private TutorDao tutorDao;
@Autowired
public void setTutorDao(TutorDao tutorDao) {
this.tutorDao = tutorDao;
}
public Tutor findTutorById(int tutorId) {
return tutorDao.findTutorById(1);
}
@Transactional
public int insertTutor(Tutor tutor) {
return tutorDao.insertTutor(tutor);
}
}
mapper接口
package com.mrq.mappers;
import com.mrq.domain.Tutor;
public interface TutorMapper {
Tutor findTutorById(int tutorId);
int insertTutor(Tutor tutor);
}
映射文件TutorMapper.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="com.mrq.mappers.TutorMapper">
<resultMap type="com.mrq.domain.Tutor" id="TutorResult">
<id property="tutorId" column="tutor_id" />
<result property="name" column="name" />
<result property="email" column="email" />
</resultMap>
<select id="findTutorById" parameterType="int" resultMap="TutorResult">
select T.tutor_id,T.name,email
from tutors T where T.tutor_id=#{tutorId}
</select>
<insert id="insertTutor" parameterType="com.mrq.domain.Tutor">
insert into tutors(name,email) values(#{name},#{email})
</insert>
</mapper>
至此,一个简单的spring和mybatis集成的小demo完成了.