映射器配置文件
映射配置文件主要完成的是对数据库的操作,我们直接将sql语句写在映射配置文件中,比如下面的例子:
<?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.mybatis3.mappers.StudentMapper">
<select id="findStudentById" parameterType="int" resultType="Student">
select stud_id as studId, name, email, dob
from Students where stud_id=#{studId}
</select>
</mapper>
上面的文件就是一个映射配置文件:
namespace
是命名空间- id是该标签的唯一标识符,在同一个命名空间中该id唯一
parameterType
是输入参数的类型resultType
是输出参数的类型
在java代码中我们想要调用该查询语句可以使用下面的代码:
public Student findStudentById(Integer studId){
//通过封装类来获取sqlSession
SqlSession sqlSession = MyBatisUtil.getSqlSession();
try{
Student student = sqlSession.selectOne("com.mybatis3.mappers.StudentMapper.
findStudentById", studId);
return student;
}finally{
sqlSession.close();
}
}
然后再通过sqlSession
的各种api
(这里使用的是selectOne
())去调用我们的sql
映射。对于这种方式只有当我们执行的时候才会知道是否出错,所以还可以使用另外一种更好的方式。
mapper映射器接口
另一种方式是当配置映射文件后,可以创建一个完全对应的映射器接口,所谓的完全对应主要有以下几个方面:
- 接口所在的包和配置文件所在的包一致
- 接口名与配置文件名一致 上面两条结合起来比如
StudentMapper.xml
所在的包名是com.mybatis.mappers
那么对应的接口名就是com.mybatis.mappers.StudentMapper.java
- 接口中的方法名就是配置文件中id值
- 方法参数类型为
parameterType
对应值 - 方法返回值类型为
returnType
对应值 - 命名空间
namespace
与接口的完全限定名一致
对应上述的StudentMapper.xml
文件,可以创建一个映射器接口StudentMapper.java
package com.mybatis.mapper;
public interface StudentMapper{
Student findStudentById(Integer id);
}
使用接口映射器
public Student findStudentById(Integer stuId){
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
return studentMapper.findStudentById(stuId);
}finally{
sqlSession.close();
}
}
映射语句
Mybatis
提供了多种元素来配置不同类型的语句,比如select
,insert
,update
,delete
,不过需要注意的是这些元素只是为了增强可读性,并不是说select
语句必须写在select
元素里面。但是为了良好的可读性还是要将对应类型的sql
语句写在对应的标签里面比较好。
insert语句
insert配置
<insert id="insertStudent" parameterType="Student">
INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)
VALUES(#{studId},#{name},#{email},#{phone})
</insert>
id
是在当前命名空间中唯一的值parameterType
是输入参数类型
insert调用
int count = sqlSession.insert("com.mybatis3.mappers.StudentMapper.insertStudent", student);
该方法返回的值是影响的行数。
insert语句自动生成主键配置
mysql
在mysql
中可以为自动生成(auto-generated
)主键的咧stud_id
插入值,可以使用useGenerateKeys
和keyProperty
属性让数据库生成auto_increment
列的值,并将生成的值设置到其中一个输入对象属性内。
<insert id="insertStudent" parameterType="Student" useGeneratedKeys="true"
keyProperty="studId">
INSERT INTO STUDENTS(NAME, EMAIL, PHONE)
VALUES(#{name},#{email},#{phone})
</insert>
配置了主键自动生成后STUD_ID
列的值将会被Mysql
数据库自动生成,并且生成的值会被设置到student
对象的studId
属性上。
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
mapper.insertStudent(student);
int studId = student.getStudId();
oracle
order=“before”
oracle
是不支持auto_increment
列,而是使用序列(sequence
)来生成主键值,假如我们有一个stud_id_seq
的序列来生成stud_id
主键值,那么可以使用如下代码来生成主键:
<insert id="insertStudent" parameterType="Student">
<selectKey keyProperty="studId" resultType="int" order="BEFORE">
SELECT STUD_ID_SEQ.NEXTVAL FROM DUAL
</selectKey>
INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE)
VALUES(#{studId},#{name},#{email},#{phone})
</insert>
使用<selectKey>子元素类生成主键值,并将值保存到Student对象的studId属性上,上面的order=“before”表示mybatis
将取得序列的下一个值作为主键值,并且在执行insert sql
语句之前将值设置到studId
属性上。
order=“after”
上面使用的是order=“before”,order的值还可以为after,获取序列的下一个值时使用触发器(trigger)来设置主键值,并且在执行insert sql语句之前将值设置到主键列上,此时的配置文件为:
<insert id="insertStudent" parameterType="Student">
INSERT INTO STUDENTS(NAME,EMAIL, PHONE)
VALUES(#{name},#{email},#{phone})
<selectKey keyProperty="studId" resultType="int" order="AFTER">
SELECT STUD_ID_SEQ.CURRVAL FROM DUAL
</selectKey>
</insert>
update
与delete
语句是类似的,所以不再写,重点是select
语句。
select语句
一个简单的查询的映射
<select id="findStudentById" parameterType="int" resultType="Student">
SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS WHERE STUD_ID=#{studId}
</select>
命名空间方式调用
int studId =1;
Student student = sqlSession.selectOne("com.mybatis3.mappers.
StudentMapper.findStudentById", studId);
映射接口调用
package com.mybatis3.mappers;
public interface StudentMapper{
Student findStudentById(Integer studId);
}
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findStudentById(studId);
查询映射需要注意的问题(属性与字段对应规则)
在上面的例子查询结果中student对象的studId属性是不会有值的,这是因为mybatis自动对javaBean中的列中和列名匹配的属性进行填充,表中是stud_id,javaBean中是studId,二者是不对应的,如果对应可以将查询结果取别名:
<select id="findStudentById" parameterType="int" resultType="Student">
SELECT STUD_ID AS studId, NAME,EMAIL, PHONE FROM STUDENTS WHERE STUD_ID=#{studId}
</select>
select返回多条结果
返回多条结果时配置文件与返回一条是一样的,resultType返回的依然是对象而不是一个集合。
<select id="findAllStudents" resultType="Student">
SELECT STUD_ID AS studId, NAME,EMAIL, PHONE FROM STUDENTS
</select>
通过命名空间调用
List<Student> students =
sqlSession.selectList("com.mybatis3.mappers.StudentMapper.findAllStudents");
通过接口映射器调用
定义接口
package com.mybatis3.mappers;
public interface StudentMapper{
List<Student> findAllStudents();
}
调用
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.findAllStudents();
结果集映射ResultMaps
ResultMaps
是用来将查询结果集映射到javaBean
的属性中。使用步骤:
- 定义一个车
ResultMap
结果集 - 在
select
标签中引用resultMap
一个简单的案例:
<resultMap id="StudentResult" type="com.mybatis3.domain.Student">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
</resultMap>
<select id="findAllStudents" resultMap="StudentResult">
SELECT * FROM STUDENTS
</select>
<select id="findStudentById" parameterType="int" resultMap="StudentResult">
SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}
</select>
注意事项:
resultMap
的id在同一个命名空间内部应该是唯一的type
属性是完全限定类名或者是返回类型的别名resultType
与resultMap
两者之间只能使用一个,不能同时使用
将结果集映射到HashMap中
如果将resultType
类型设置为map(java.util.HashMap)
,结果集的列名将会作为Map的key值,列值作为Map的value值。
返回单个结果
配置文件:
<select id="findStudentById" parameterType="int" resultType="map">
SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}
</select>
部分测试代码:
HashMap<String,Object> studentMap = sqlSession.selectOne("com.
mybatis3.mappers.StudentMapper.findStudentById", studId);
System.out.println("stud_id :"+studentMap.get("stud_id"));
System.out.println("name :"+studentMap.get("name"));
System.out.println("email :"+studentMap.get("email"));
System.out.println("phone :"+studentMap.get("phone"));
返回多个结果
配置文件:
<select id="findAllStudents" resultType="map">
SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS
</select>
部分测试代码:
List<HashMap<String, Object>> studentMapList =
sqlSession.selectList("com.mybatis3.mappers.StudentMapper.findAllS
tudents");
for(HashMap<String, Object> studentMap : studentMapList){
System.out.println("studId :" + studentMap.get("stud_id"));
System.out.println("name :" + studentMap.get("name"));
System.out.println("email :" + studentMap.get("email"));
System.out.println("phone :" + studentMap.get("phone"));
}
拓展ResultMap
这里的拓展类似于java中的继承。
配置文件案例:
<!--这里type直接写student是因为有了别名-->
<resultMap type="Student" id="StudentResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
</resultMap>
<resultMap type="Student" id="StudentWithAddressResult" extends="StudentResult">
<result property="address.addrId" column="addr_id" />
<result property="address.street" column="street" />
<result property="address.city" column="city" />
<result property="address.state" column="state" />
<result property="address.zip" column="zip" />
<result property="address.country" column="country" />
</resultMap>
id为StudentWithAddressResult
的ResultMap
继承了id
为StudentResult
的ResultMap
这样如果只想映射Student
的话那么可以只使用id
为StudentResult
的ResultMap
就可以,但是如果想要映射Student
与Address
数据,那么就可以使用id为StudentWithAddressResult
的ResultMap
。
一对一映射
基本一对一映射案例
比如现在有两个表Student
与Address
关系如下:
Student表:
stud_id | name | phone | addr_id | |
---|---|---|---|---|
1 | 张三 | hrgqg@qq.com | 12413 | 1 |
2 | 李四 | a1232@qq.com | 3545 | 2 |
Address表:
addr_id | streef | city | state | zip | country |
---|---|---|---|---|---|
1 | jafgoi | HK | 7839 | 1398 | usa |
2 | hairog | HK | 8193 | 89131 | usa |
这两个表是有关联的,Student
表与Address
表之间的关联是Student
表中的addr_id
是外键。
此时的javaBean
代码如下:
AddressBean
:
public class Address{
private Integer addrId;
private String street;
private String city;
private String state;
private String zip;
private String country;
// setters & getters
}
StudentBean
:
public class Student{
private Integer studId;
private String name;
private String email;
private PhoneNumber phone;
private Address address;
//setters & getters
}
配置文件:
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
<result property="address.addrId" column="addr_id" />
<result property="address.street" column="street" />
<result property="address.city" column="city" />
<result property="address.state" column="state" />
<result property="address.zip" column="zip" />
<result property="address.country" column="country" />
</resultMap>
<select id="selectStudentWithAddress" parameterType="int"
resultMap="StudentWithAddressResult">
SELECT STUD_ID, NAME, EMAIL, A.ADDR_ID, STREET, CITY, STATE,
ZIP, COUNTRY FROM STUDENTS S LEFT OUTER JOIN ADDRESSES A ON
S.ADDR_ID=A.ADDR_ID WHERE STUD_ID=#{studId}
</select>
由于在Student
对象中包含Address
对象,所以在配置文件中可以使用圆点记法为内嵌对象赋值。
上面这种方式如果想要查询Address
就相当于也要查询Student
,这样会有重复映射,此时可以使用嵌套结果ResultMap
和嵌套select
查询语句。
嵌套ResultMap来实现一对一映射
has-one关联
依然是上面的案例,此时的配置文件如下:
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<association property="address" resultMap="AddressResult" />
</resultMap>
<select id="findStudentWithAddress" parameterType="int"
resultMap="StudentWithAddressResult">
SELECT STUD_ID, NAME, EMAIL, A.ADDR_ID, STREET, CITY, STATE,
ZIP, COUNTRY FROM STUDENTS S LEFT OUTER JOIN ADDRESSES A ON
S.ADDR_ID=A.ADDR_ID WHERE STUD_ID=#{studId}
</select>
元素<association>被用来导入“has-one”类型的关联,在上面的例子中使用<association>元素引用了在同一个xml
文件中定义的<resultMap>(resultMap
的值是另外一个ResultMap
的id
)
内联关联
内联关联配置文件如下:
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<association property="address" javaType="Address">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</association>
</resultMap>
使用嵌套Select来实现一对一映射
配置文件:
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<select id="findAddressById" parameterType="int"
resultMap="AddressResult">
SELECT * FROM ADDRESSES WHERE ADDR_ID=#{id}
</select>
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<association property="address" column="addr_id" select="findAddressById" />
</resultMap>
<select id="findStudentWithAddress" parameterType="int"
resultMap="StudentWithAddressResult">
SELECT * FROM STUDENTS WHERE STUD_ID=#{Id}
</select>
在此方式中,<association>元素的 select
属性被设置成了 id
为 findAddressById
的语句。这里,两个分开的SQL
语句将会在数据库中执行,第一个调用 findStudentById
加载 student
信息,而第二个调用 findAddressById
来加载 address
信息。Addr_id
列的值将会被作为输入参数传递给 selectAddressById
语句。
调用语句:
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findStudentWithAddress(studId);
System.out.println(student);
System.out.println(student.getAddress());
一对多映射
案例表数据
tutors
:
tutor_id | tutor_name | phone | addr_id | |
---|---|---|---|---|
1 | 张三 | ad@qq.com | 3213 | 1 |
2 | 李四 | asg@qq.com | 1525 | 2 |
course:
course_id | name | description | start_date | end_date | tutor_id |
---|---|---|---|---|---|
1 | javaSE | java Se | 2019-01-01 | 2019-01-01 | 1 |
2 | javaEE | java Ee | 2019-01-01 | 2019-01-01 | 2 |
3 | Mybatis | Mybatis | 2019-01-01 | 2019-01-01 | 2 |
上面教授与课程表是一对多(外键在多的一方)的关联,可以看到李四对应两门课程。
tutorBean
public class Tutor{
private Integer tutorId;
private String name;
private String email;
private Address address;
private List<Course> courses;
// setters & getters
}
CourseBean
public class Course{
private Integer courseId;
private String name;
private String description;
private Date startDate;
private Date endDate;
private Integer tutorId;
//setters & getters
}
使用内嵌结果ResultMap实现一对多
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="courseId" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="start_date" property="startDate" />
<result column="end_date" property="endDate" />
</resultMap>
<resultMap type="Tutor" id="TutorResult">
<id column="tutor_id" property="tutorId" />
<result column="tutor_name" property="name" />
<result column="email" property="email" />
<collection property="courses" resultMap="CourseResult" />
</resultMap>
<select id="findTutorById" parameterType="int" resultMap="TutorResult">
SELECT T.TUTOR_ID as tutorId , T.tutor_NAME AS NAME, EMAIL,
C.COURSE_ID as courseId, C.NAME, DESCRIPTION, START_DATE as
StartDate, END_DATE as endDate
FROM TUTORS T LEFT OUTER JOIN ADDRESSES A ON T.ADDR_ID=A.ADDR_ID
LEFT OUTER JOIN COURSES C ON T.TUTOR_ID=C.TUTOR_ID
WHERE T.TUTOR_ID=#{tutorId}
</select>
使用嵌套Select语句实现一对多映射
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="courseId" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="start_date" property="startDate" />
<result column="end_date" property="endDate" />
</resultMap>
<resultMap type="Tutor" id="TutorResult">
<id column="tutor_id" property="tutorId" />
<result column="tutor_name" property="name" />
<result column="email" property="email" />
<association property="address" resultMap="AddressResult" />
<collection property="courses" column="tutor_id" select="findCoursesByTutor" />
</resultMap>
<select id="findTutorById" parameterType="int" resultMap="TutorResult">
SELECT T.TUTOR_ID tutorId, T.TUTOR_NAME AS NAME, EMAIL
FROM TUTORS T WHERE T.TUTOR_ID=#{tutorId}
</select>
<select id="findCoursesByTutor" parameterType="int"
resultMap="CourseResult">
SELECT * FROM COURSES WHERE TUTOR_ID=#{tutorId}
</select>
==注:嵌套 Select 语句查询会导致 N+1 选择问题。首先,主查询将会执行(1 次),对于主 查询返回的每一行,另外一个查询将会被执行(主查询 N 行,则此查询 N 次)。对于 大型数据库而言,这会导致很差的性能问题。==
动态sql语句
类似淘宝的查询功能就需要动态的构建sql
语句(没有输入条件的时候不会有那个查询的SQL
,有查询条件的时候就需要叫上那一句),mybatis
拖过使用<if>、<choose>、<where>、<foreach>、<trim>元素来对构造动态sql
语句提供支持。
if条件
<if>元素被用来有条件的嵌入sql
片段,如果测试条件为true
,则相应的sql
片段会被添加到sql
语句中。
配置文件:
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="courseId" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="start_date" property="startDate" />
<result column="end_date" property="endDate" />
</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 >= #{startDate}
</if>
<if test="endDate != null">
AND END_DATE <= #{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);
}
}
==mybatis
使用ONGL(object Graph Navigation Language)
表达式来构建动态sql
语句==
choose,when以及otherwise
当以类别为基础来查询时可以使用choose
配置文件
<select id="searchCourses" 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 TUTOR start_date >= now()
</otherwise>
</choose>
</select>
where
当所有条件都是可选时可以使用where
子句,如果有多个条件还可以使用and或or。
<select id="searchCourses" 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 >= #{startDate}
</if>
<if test="endDate != null">
AND end_date <= #{endDate}
</if>
</where>
</select>
<where>元素只有在其内部标签有返回内容时才会在动态sql
语句上插入where
条件语句,而且【1如果where
子句以and
或者or
开始,那么and
或者or
会被移除。
上面的配置文件中parameterType
都是hashMap
,所以下面的条件中的参数比如tutorId
以及coursName
和startDate
都是map
里面的key
。而且条件与参数的用法是不同的,对于参数的话是使用#{},条件是包含在test
里面。
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
。
foreach
使用foreach构造and/or条件
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES
<if test="tutorIds != null">
<where>
<foreach item="tutorId" collection="tutorIds">
OR tutor_id=#{tutorId}
</foreach>
</where>
</if>
</select>
接口
public interface CourseMapper{
List<Course> searchCoursesByTutors(Map<String, Object> map);
}
测试代码
public void searchCoursesByTutors(){
Map<String, Object> map = new HashMap<String, Object>();
List<Integer> tutorIds = new ArrayList<Integer>();
tutorIds.add(1);
tutorIds.add(3);
tutorIds.add(6);
map.put("tutorIds", tutorIds);
CourseMapper mapper =
sqlSession.getMapper(CourseMapper.class);
List<Course> courses = mapper.searchCoursesByTutors(map);
for (Course course : courses){
System.out.println(course);
}
}
上面的案例是查询tutor_id
为1,3,6的数据。同样的对于foreach
子句而言如果生成的where
子句第一个条件前面是and
或者or``则会移除掉
。
使用foreach构造in子句
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES
<if test="tutorIds != null">
<where>
tutor_id IN
<foreach item="tutorId" collection="tutorIds"
open="(" separator="," close=")">
#{tutorId}
</foreach>
</where>
</if>
</select>
set条件
<set>与<where>元素类似,如果内部的条件判断有任何内容返回时会插入set sql
片段。
<update id="updateStudent" parameterType="Student">
update students
<set>
<if test="name != null">name=#{name},</if>
<if test="email != null">email=#{email},</if>
<if test="phone != null">phone=#{phone},</if>
</set>
where stud_id=#{id}
</update>
即使是最后一个条件,后面也是有逗号的。如果<if>条件返回了任何文本内容,<set>将会插入 set 关键字和其文本内容,并且会剔除将末尾的 “,”。 在上述的例子中,如果 phone!=null
,<set>将会让会移除 phone=#{phone}
后的逗号“,”, 生成 set phone=#{phone}
。
传入多个参数
mybatis
中的映射语句中有一个车parameterType
属性来指定输入参数的类型,当我们想要给映射语句传入多个参数晒时候,可以将所有的输入参数映射到HashMap
中,然后将HashMap
传递给映射语句。还有第二种方式,看下面的例子。
接口:
public interface StudentMapper{
List<Student> finAllStdentsByNameEmail(String name, String email);
}
映射文件:
<select id="findAllStudentsByNameEmail" resultMap="StudentResult">
select stud_id, name,email, phone from Students
where name=#{param1} and email=#{param2}
</select>
mybatis
支持将多个输入参数传递给映射语句,并以#{param}
的语法形式来引用。上面的例子中#{param1}
引用第一个参数name
,#{param2}
引用第二个参数email
。
调用查询:
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
studentMapper.findAllStudentsByNameEmail(name, email);
多行结果集映射成Map
如果你有一个映射语句返回多行记录,并且你想以 HashMap
的形式存储记录的值,使用记录列名作为 key
值,而记录对应值或为 value
值。我们可以使用 sqlSession.selectMap()
,如下所示:
配置文件:
<select id=" findAllStudents" resultMap="StudentResult">
select * from Students
</select>
查询:
Map<Integer, Student> studentMap =
sqlSession.selectMap("com.mybatis3.mappers.StudentMapper.findAllStudents", "studId");
上面的例子中会使用studentId
作为key
值,Student
对象作为value
值。
分页查询
可以使用RowBounds
来逐页逐页的加载数据,该对象有两个参数:
offset
: 表示开始位置limit
:表示要取的记录的数量
配置文件:
<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
。
使用ResultSetHandler自定义结果集ResultSet处理
上面在使用selectMap()
时是将给定的列作为key
,将记录对象(Student
)作为value
,但是并不能配置将一个属性作为key
,将另外一个属性作为value
。
==mybatis-3.2.2
并不支持使用 resultMap
配置将查询的结果集映射成一个属性为 key
,而另外属性为 value
的 HashMap
==
对于像上面所述(一个属性映射为key
,一个属性映射为value
)可以使用ResultSetHandler
插件来自定义结果集。
接口:
public interface ResultHandler{
void handleResult(ResultContext context);
}
调用代码:
public Map<Integer, String> getStudentIdNameMap(){
final Map<Integer, String> map = new HashMap<Integer, String>();
SqlSession sqlSession = MyBatisUtil.openSession();
try{
sqlSession.select("com.mybatis3.mappers.StudentMapper.findAllStude nts",
new ResultHandler(){
@Override
public void handleResult(ResultContext context){
Student student = (Student) context.getResultObject();
map.put(student.getStudId(), student.getName());
}
});
}finally{
sqlSession.close();
}
return map;
}
在使用sqlSession.select()
方法时,传递的参数中有一个是ResultHandler
的实现(上面的代码是通过匿名函数来实现的),它会被调用来处理ResultSet
的某一行记录。
在ResultHandler
的匿名实现类中context.getResultObject()
是获取当前的Result
对象(Student
对象,因为我们定义了findAllStudents
映射语句中的resultMap=StudentResult
),对每一行返回的结果都会调用handleResult()
方法,并且从Student
中取出studId
和name
将其放入到map
中。
缓存
mybatis
对通过映射的select语句加载的查询结果提供了内建的缓存支持。默认情况下启用的是一级缓存(sqlSession
级别),也就是如果使用同一个sqlSession
接口对象调用了相同的select
语句,则直接回从缓存中返回结果,而不是再一次查询数据库。
可以在sql
映射文件中使用<cache>元素来配置全局二级缓存。当配置二级缓存后会出现以下情形:
- 所有的映射语句文件定义的<select>语句的查询结果都会被缓存
- 所有的在映射语句文件中定义的<insert>,<update>,<delete>语句都会刷新缓存。
- 缓存根据左最近最少被使用算法管理
- 缓存将存储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
(weak reference
,弱引用)。
flushInterval
:定义缓存刷新间隔,以毫秒计。默认情况下不设置。所以不使用刷新间隔,缓存cache
只有调用语句的时候刷新。- 此表示缓存
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”
。