mybatis的sql映射文件

映射器配置文件

映射配置文件主要完成的是对数据库的操作,我们直接将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提供了多种元素来配置不同类型的语句,比如selectinsertupdatedelete,不过需要注意的是这些元素只是为了增强可读性,并不是说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插入值,可以使用useGenerateKeyskeyProperty属性让数据库生成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>

updatedelete语句是类似的,所以不再写,重点是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属性是完全限定类名或者是返回类型的别名
  • resultTyperesultMap两者之间只能使用一个,不能同时使用

将结果集映射到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为StudentWithAddressResultResultMap继承了idStudentResultResultMap

这样如果只想映射Student的话那么可以只使用idStudentResultResultMap就可以,但是如果想要映射StudentAddress数据,那么就可以使用id为StudentWithAddressResultResultMap

一对一映射

基本一对一映射案例

比如现在有两个表StudentAddress关系如下:

Student表:

stud_idnameemailphoneaddr_id
1张三hrgqg@qq.com124131
2李四a1232@qq.com35452

Address表:

addr_idstreefcitystatezipcountry
1jafgoiHK78391398usa
2hairogHK819389131usa

这两个表是有关联的,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的值是另外一个ResultMapid)

内联关联

内联关联配置文件如下:

<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 属性被设置成了 idfindAddressById 的语句。这里,两个分开的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_idtutor_nameemailphoneaddr_id
1张三ad@qq.com32131
2李四asg@qq.com15252

course:

course_idnamedescriptionstart_dateend_datetutor_id
1javaSEjava Se2019-01-012019-01-011
2javaEEjava Ee2019-01-012019-01-012
3MybatisMybatis2019-01-012019-01-012

上面教授与课程表是一对多(外键在多的一方)的关联,可以看到李四对应两门课程。

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以及coursNamestartDate都是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 后面的 ANDOR

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来逐页逐页的加载数据,该对象有两个参数:

  1. offset: 表示开始位置
  2. 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,而另外属性为 valueHashMap==

对于像上面所述(一个属性映射为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中取出studIdname将其放入到map中。

缓存

mybatis对通过映射的select语句加载的查询结果提供了内建的缓存支持。默认情况下启用的是一级缓存(sqlSession级别),也就是如果使用同一个sqlSession接口对象调用了相同的select语句,则直接回从缓存中返回结果,而不是再一次查询数据库。

可以在sql映射文件中使用<cache>元素来配置全局二级缓存。当配置二级缓存后会出现以下情形:

  • 所有的映射语句文件定义的<select>语句的查询结果都会被缓存
  • 所有的在映射语句文件中定义的<insert>,<update>,<delete>语句都会刷新缓存。
  • 缓存根据左最近最少被使用算法管理
  • 缓存将存储1024个查询方法返回的列表或对象的引用
  • 缓存会被当做一个读/写缓存。即检索出的对象不会被共享,并且可以被调用者安全的修改,不会被其他潜在的调用者或者线程的潜在修改干扰(缓存是线程安全的)。

可以通过复写默认属性来自定义缓存行为:

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>

对上面配置的缓存的解释:

  • eviction:此处定义缓存的移除机制。默认值是 LRU,其可能的值有:
    • LRUleast recently used,最近最少使用)。
    • FIFO(first in first out,先进先出)。
    • SOFT(soft reference,软引用)。
    • WEAKweak reference,弱引用)。
  • flushInterval:定义缓存刷新间隔,以毫秒计。默认情况下不设置。所以不使用刷新间隔,缓存 cache 只有调用语句的时候刷新。
  • 此表示缓存 cache 中能容纳的最大元素数。默认值是 1024,你可以设置成任意的正整数
  • readOnly:一个只读的缓存 cache 会对所有的调用者返回被缓存对象的同一个实例(实际返回的是被返回对象的一份引用)。一个读/写缓存 cache 将会返回被返回对象的一分拷贝(通过序列化)。默认情况下设置为 false。可能的值有 falsetrue

一个缓存的配置和缓存实例被绑定到映射器配置文件所在的名空间(namespace)上,所以在相同名空间内的所有语句被绑定到一个 cache 中。

默认的映射语句的cache配置如下:

<select ... flushCache="false" useCache="true"/>
<insert ... flushCache="true"/>
<update ... flushCache="true"/>
<delete ... flushCache="true"/>

可以为任意特定的映射语句复写默认的 cache 行为;例如,对一个 select 语句不使用缓存,可以设置useCache=“false”

转载于:https://my.oschina.net/guowei11/blog/3075499

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值