您的位置:首页 > 博客中心 > 数据库 >

Java Persistence with MyBatis 3(中文版) 第三章 使用XML配置SQL映射器

时间:2022-03-10 17:48

    关系型数据库和SQL是经受时间考验和验证的数据存储机制。和其他的ORM 框架如Hibernate不同,MyBatis鼓励开发者可以直接使用数据库,而不是将其对开发者隐藏,因为这样可以充分发挥数据库服务器所提供的SQL语句的巨大威力。与此同时,MyBaits消除了书写大量冗余代码的痛苦,它使使用SQL更容易。

    在代码里直接嵌套SQL语句是很差的编码实践,并且维护起来困难。MyBaits使用了映射器配置文件或注解来配置SQL语句。在本章中,我们会看到具体怎样使用映射器配置文件来配置映射SQL语句。

 

本章将涵盖以下话题:

  • l  映射器配置文件和映射器接口
  • l  映射语句

           ž 配置INSERT, UPDATE, DELETE,and SELECT语句

  • l  结果映射ResultMaps

           ž 简单 ResultMaps

           ž 使用内嵌select语句子查询的一对一映射

           ž 使用内嵌的结果集查询的一对一映射

           ž 使用内嵌select语句子查询的一对多映射

           ž 使用内嵌的结果集查询的一对一映射

  • l  动态SQL语句

           ž If 条件

           ž choose (when, otherwise) 条件

           ž trim (where, set) 条件

           ž foreach 循环

  • l  MyBatis 菜谱

<?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>

我们可以通过下列代码调用findStudentById映射的SQL语句:

public Student findStudentById(Integer studId)
{
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    try
    {
        Student student =
            sqlSession.selectOne("com.mybatis3.mappers.StudentMapper.
                                 findStudentById", studId);
        return student;
    }
    finally
    {
        sqlSession.close();
    }
}

    我们可以通过字符串(字符串形式为:映射器配置文件所在的包名namespace + 在文件内定义的语句id,如上,即包名com.mybatis3.mappers.StudentMapper 和语句id findStudentById 组成)调用映射的SQL语句,但是这种方式容易出错。你需要检查映射器配置文件中的定义,以保证你的输入参数类型和结果返回类型是有效的。

    MyBatis通过使用映射器Mapper接口提供了更好的调用映射语句的方法。一旦我们通过映射器配置文件配置了映射语句,我们可以创建一个完全对应的一个映射器接口,接口名跟配置文件名相同,接口所在包名也跟配置文件所在包名完全一样(如StudentMapper.xml所在的包名是com.mybatis3.mappers,对应的接口名就是com.mybatis3.mappers.StudentMapper.java)。映射器接口中的方法签名也跟映射器配置文件中完全对应:方法名为配置文件中id值;方法参数类型为parameterType对应值;方法返回值类型为returnType对应值。

 

    对于上述的 StudentMapper.xml 文件,我们可以创建一个映射器接口StudentMapper.java 如下:

package com.mybatis3.mappers;
public interface StudentMapper
{
    Student findStudentById(Integer id);
}

    在StudentMapper.xml 映射器配置文件中,其名空间namespace 应该跟StudentMapper接口的完全限定名保持一致。另外,StudentMapper.xml中语句id,parameterType,returnType应该分别和StudentMapper接口中的方法名,参数类型,返回值相对应。

 

    使用映射器接口我们可以以类型安全的形式调用调用映射语句。如下所示:

	public Student findStudentById(Integer studId)
{
    SqlSession sqlSession = MyBatisUtil.getSqlSession();
    try
    {
        StudentMapper studentMapper =
            sqlSession.getMapper(StudentMapper.class);
        return studentMapper.findStudentById(studId);
    }
    finally
    {
        sqlSession.close();
    }
}

gxlsystem.com,布布扣

<insert id="insertStudent" parameterType="Student"> INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL, PHONE) VALUES(#{studId},#{name},#{email},#{phone}) </insert>

    这里我们使用一个ID insertStudent,可以在名空间com.mybatis3.mappers.StudentMapper.insertStudent中唯一标识。parameterType属性应该是一个完全限定类名或者是一个类型别名(alias)。

 

我们可以如下调用这个语句:
int count =sqlSession.insert("com.mybatis3.mappers.StudentMapper.insertStudent", student);

sqlSession.insert()方法返回执行INSERT语句后所影响的行数。

如果不使用名空间(namespace)和语句id来调用映射语句,你可以通过创建一个映射器Mapper接口,并以类型安全的方式调用方法,如下所示:

package com.mybatis3.mappers;
public interface StudentMapper
{
    int insertStudent(Student student);
}

你可以如下调用 insertStudent 映射语句:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int count = mapper.insertStudent(student);

<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);

现在可以如下获取插入的STUDENT 记录的STUD_ID的值:

int studentId = student.getStudId();

有些数据库如Oracle并不支持 AUTO_INCREMENT 列,其使用序列(SEQUENCE)来生成主键值。

假设我们有一个名为 STUD_ID_SEQ 的序列来生成SUTD_ID主键值。使用如下代码来生成主键:

<insert id="insertStudent" parameterType="Student">
    <selectKey keyProperty="studId" resultType="int" order="BEFORE">
        SELECT ELEARNING.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将取得序列的下一个值作为主键值,并且在执行INSERTSQL语句之前将值设置到studId属性上。

    我们也可以在获取序列的下一个值时,使用触发器(trigger)来设置主键值,并且在执行INSERT SQL语句之前将值设置到主键列上。如果你采取这样的方式,则对应的INSERT映射语句如下所示:

<insert id="insertStudent" parameterType="Student">
    INSERT INTO STUDENTS(NAME,EMAIL, PHONE)
        VALUES(#{name},#{email},#{phone})
    <selectKey keyProperty="studId" resultType="int" order="AFTER">
        SELECT ELEARNING.STUD_ID_SEQ.CURRVAL FROM DUAL
    </selectKey>
</insert>

<update id="updateStudent" parameterType="Student"> UPDATE STUDENTS SET NAME=#{name}, EMAIL=#{email}, PHONE=#{phone} WHERE STUD_ID=#{studId} </update>

我们可以如下调用此语句:

int noOfRowsUpdated =
sqlSession.update("com.mybatis3.mappers.StudentMapper.updateStudent", student);

sqlSession.update() 方法返回执行UPDATE语句之后影响的行数。

如果不使用名空间(namespace)和语句id来调用映射语句,你可以通过创建一个映射器Mapper接口,并以类型安全的方式调用方法,如下所示:

package com.mybatis3.mappers;
public interface StudentMapper
{
    int updateStudent(Student student);
}

你可以使用映射器Mapper接口来调用 updateStudent语句,如下所示:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int noOfRowsUpdated = mapper.updateStudent(student);

<delete id="deleteStudent" parameterType="int"> DELETE FROM STUDENTS WHERE STUD_ID=#{studId} </delete>

我们可以如下调用此语句:

int studId = 1;
int noOfRowsDeleted =
    sqlSession.delete("com.mybatis3.mappers.StudentMapper.deleteStudent", studId);

sqlSession.delete()方法返回delete语句执行后影响的行数。

 

如果不使用名空间(namespace)和语句id来调用映射语句,你可以通过创建一个映射器Mapper接口,并以类型安全的方式调用方法,如下所示:

package com.mybatis3.mappers;
public interface StudentMapper
{
    int deleteStudent(int studId);
}

你可以使用映射器Mapper接口来调用 updateStudent语句,如下所示:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
int noOfRowsDeleted = mapper.deleteStudent(studId);

<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);

如果不使用名空间(namespace)和语句id来调用映射语句,你可以通过创建一个映射器Mapper接口,并以类型安全的方式调用方法,如下所示:

package com.mybatis3.mappers;
public interface StudentMapper
{
    Student findStudentById(Integer studId);
}

你可以使用映射器Mapper接口来调用 updateStudent语句,如下所示:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findStudentById(studId);

    如果你检查Student对象的属性值,你会发现studId属性值并没有被stud_id列值填充。这是因为MyBatis自动对JavaBean中和列名匹配的属性进行填充。这就是为什么name ,email,和phone属性被填充,而studId属性没有被填充。

    为了解决这一问题,我们可以为列名起一个可以与JavaBean中属性名匹配的别名,如下所示:

<select id="findStudentById" parameterType="int" 
resultType="Student">
    SELECT STUD_ID AS studId, NAME,EMAIL, PHONE 
        FROM STUDENTS 
    WHERE STUD_ID=#{studId}
</select>

现在,Student 这个Bean对象中的值将会恰当地被stud_id,name,email,phone列填充了。

现在,让我们看一下如何执行返回多条结果的SELECT语句查询,如下所示:

<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");

映射器Mapper接口StudentMapper可以如下定义:

package com.mybatis3.mappers;
public interface StudentMapper
{
    List<Student> findAllStudents();
}

使用上述代码,我们可以如下调用 findAllStudents语句:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.findAllStudents();

如果你注意到上述的SELECT映射定义,你可以看到,我们为所有的映射语句中的stud_id起了别名。

我们可以使用ResultMaps,来避免上述的到处重复别名。我们稍后会继续讨论。

除了java.util.List,你也可以是由其他类型的集合类,如Set,Map,以及(SortedSet)。MyBatis根据集合的类型,会采用适当的集合实现,如下所示:

  • l 对于List,Collection,Iterable类型,MyBatis将返回java.util.ArrayList
  • l 对于Map类型,MyBatis将返回java.util.HashMap
  • l 对于Set类型,MyBatis将返回 java.util.HashSet
  • l 对于SortedSet类型,MyBatis将返回java.util.TreeSet

<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的StudentResult id值应该在此名空间内是唯一的。并且type属性应该是完全限定类名或者是返回类型的别名。

<result>子元素被用来将一个resultset列映射到JavaBean的一个属性中。

<id>元素和<result>元素功能相同,不过它被用来映射到唯一标识属性,用来区分和比较对象(一般和主键列相对应)。

在<select>语句中,我们使用了resultMap属性,而不是resultType来引用StudentResult映射。当<select>语句中配置了resutlMap属性,MyBatis会使用此数据库列名与对象属性映射关系来填充JavaBean中的属性。

gxlsystem.com,布布扣

 让我们来看另外一个<select>映射语句定义的例子,怎样将查询结果填充到HashMap中。如下所示:

<select id="findStudentById" parameterType="int" resultType="map">
    SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}
</select>

在上述的<select>语句中,我们将resultType配置成map,即java.util.HashMap的别名。在这种情况下,结果集的列名将会作为Map中的key值,而列值将作为Map的value值。

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"));

让我们再看一个 使用resultType=”map”,返回多行结果的例子:

<select id="findAllStudents" resultType="map">
    SELECT STUD_ID, NAME, EMAIL, PHONE FROM STUDENTS
</select>

由于resultType=”map”和语句返回多行,则最终返回的数据类型应该是List<HashMap<String,Object>>,如下所示:

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 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,如下所示:

<select id="findStudentById" parameterType="int" 
resultMap="StudentResult">
    SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}
</select>

如果你想将映射Student数据和Address数据,你可以使用id为StudentWithAddressResult的resultMap:

<select id="selectStudentWithAddress" parameterType="int" 
resultMap="StudentWithAddressResult">
SELECT STUD_ID, NAME, EMAIL, PHONE, 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>

STUD_ID

NAME

EMAIL

PHONE

ADDR_ID

1

John

john@gmail.com

123-456-7890

1

2

Paul

paul@gmail.com

111-222-3333

2

 

ADDRESSES表的样例输入如下所示:

ADDR_ID

STREET

CITY

STATE

ZIP

COUNTRY

1

Naperville

CHICAGO

IL

60515

USA

2

Paul

CHICAGO

IL

60515

USA

 

下面让我们看一下怎样取Student明细和其Address明细。

 

Student和Address 的JavaBean以及映射器Mapper XML文件定义如下所示:

public class Address
{
    private Integer addrId;
    private String street;
    private String city;
    private String state;
    private String zip;
    private String country;
    // setters & getters
}
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>

    我们可以使用圆点记法为内嵌的对象的属性赋值。在上述的resultMap中,Student的address属性使用了圆点记法被赋上了address对应列的值。同样地,我们可以访问任意深度的内嵌对象的属性。我们可以如下访问内嵌对象属性:
//接口定义
public interface StudentMapper
{
    Student selectStudentWithAddress(int studId);
}


//使用
int studId = 1;
StudentMapper studentMapper =
    sqlSession.getMapper(StudentMapper.class);
Student student = studentMapper.selectStudentWithAddress(studId);
System.out.println("Student :" + student);
System.out.println("Address :" + student.getAddress());

    上述样例展示了一对一关联映射的一种方法。然而,使用这种方式映射,如果address结果需要在其他的SELECT映射语句中映射成Address对象,我们需要为每一个语句重复这种映射关系。MyBatis提供了更好地实现一对一关联映射的方法:嵌套结果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> <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>。

我们也可以使用<association定义内联的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" 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>

使用嵌套结果ResultMap方式,关联的数据可以通过简单的查询语句(如果需要的话,需要与joins 连接操作配合)进行加载。

 

<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语句。

 我们可以如下调用findStudentWithAddress映射语句:

StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.selectStudentWithAddress(studId);
System.out.println(student);
System.out.println(student.getAddress());

TUTOR_ID

NAME

EMAIL

PHONE

ADDR_ID

1

John

john@gmail.com

123-456-7890

1

2

Ying

ying@gmail.com

111-222-3333

2

COURSE表的样例数据如下:

COURSE_ID

NAME

DESCRIPTION

START_DATE

END_DATE

TUTOR_ID

1

JavaSE

Java SE

2013-01-10

2013-02-10

1

2

JavaEE

Java EE 6

2013-01-10

2013-03-10

2

3

MyBatis

MyBatis

2013-01-10

2013-02-20

2

 

在上述的表数据中,John讲师教授一个课程,而Ying讲师教授两个课程。

 

Course和Tutor的JavaBean定义如下:

public class Course
{
    private Integer courseId;
    private String name;
    private String description;
    private Date startDate;
    private Date endDate;
    private Integer tutorId;
    //setters & getters
}
public class Tutor
{
    private Integer tutorId;
    private String name;
    private String email;
    private Address address;
    private List<Course> courses;
    / setters & getters
}

现在让我们看看如何获取讲师信息以及其所教授的课程列表信息。

<collection>元素被用来将多行课程结果映射成一个课程Course对象的一个集合。和一对一映射一样,我们可以使用嵌套结果ResultMap嵌套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" /> <collection property="courses" resultMap="CourseResult" /> </resultMap> <select id="findTutorById" parameterType="int" resultMap="TutorResult"> SELECT T.TUTOR_ID, T.NAME AS TUTOR_NAME, EMAIL, C.COURSE_ID, C.NAME, DESCRIPTION, START_DATE, END_DATE 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>

这里我们使用了一个简单的使用了JOINS连接的Select语句获取讲师及其所教课程信息。<collection>元素的resultMap属性设置成了CourseResult,CourseResult包含了Course对象属性与表列名之间的映射。

<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, T.NAME AS TUTOR_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>

    在这种方式中,<aossication>元素的select属性被设置为id 为findCourseByTutor的语句,用来触发单独的SQL查询加载课程信息。tutor_id这一列值将会作为输入参数传递给findCouresByTutor语句。

public interface TutorMapper
{
    Tutor findTutorById(int tutorId);
}
TutorMapper mapper = sqlSession.getMapper(TutorMapper.class);
Tutor tutor = mapper.findTutorById(tutorId);
System.out.println(tutor);
List<Course> courses = tutor.getCourses();
for (Course course : courses)
{
    System.out.println(course);
}
gxlsystem.com,布布扣

<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> 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);
    }
}

此处将生成查询语句 SELECT * FROM COURSES WHERE TUTOR_ID= ? AND NAME like? AND START_DATE >= ?。准备根据给定条件的动态SQL查询将会派上用场。

gxlsystem.com,布布扣

<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>

MyBatis计算<choose>测试条件的值,且使用第一个值为TRUE的子句。如果没有条件为true,则使用<otherwise>内的子句。

<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>元素只有在其内部标签有返回内容时才会在动态语句上插入WHERE条件语句。并且,如果WHERE子句以AND或者OR打头,则打头的AND或OR将会被移除。

如果tutor_id参数值为null,并且courseName参数值不为null,则<where>标签会将AND name like#{courseName} 中的AND移除掉,生成的SQL WHERE子句为:where name like#{courseName}。

<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。

<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);
    }
}

现在让我们来看一下怎样使用<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>

<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} 。


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.mybatis3.domain.Gender"/>
gxlsystem.com,布布扣

CREATE TABLE USER_PICS ( ID INT(11) NOT NULL AUTO_INCREMENT, NAME VARCHAR(50) DEFAULT NULL, PIC BLOB, BIO LONGTEXT, PRIMARY KEY (ID) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=LATIN1;

    这里,照片可以是PNG,JPG或其他格式的。简介信息可以是学生或者讲师的漫长的人生经历。默认情况下,MyBatis将CLOB类型的列映射到java.lang.String类型上、而把BLOB列映射到byte[] 类型上。

public class UserPic
{
    private int id;
    private String name;
    private byte[] pic;
    private String bio;
    //setters & getters
}

创建UserPicMapper.xml文件,配置映射语句,代码如下:

<insert id="insertUserPic" parameterType="UserPic">
    INSERT INTO USER_PICS(NAME, PIC,BIO)
    VALUES(#{name},#{pic},#{bio})
</insert>
<select id="getUserPic" parameterType="int" resultType="UserPic">
    SELECT * FROM USER_PICS WHERE ID=#{id}
</select>

下列的insertUserPic()展示了如何将数据插入到CLOB/BLOB类型的列上:

public void insertUserPic()
{
    byte[] pic = null;
    try
    {
        File file = new File("C:\\Images\\UserImg.jpg");
        InputStream is = new FileInputStream(file);
        pic = new byte[is.available()];
        is.read(pic);
        is.close();
    }
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
    String name = "UserName";
    String bio = "put some lenghty bio here";
    UserPic userPic = new UserPic(0, name, pic , bio);
    SqlSession sqlSession = MyBatisUtil.openSession();
    try
    {
        UserPicMapper mapper =
            sqlSession.getMapper(UserPicMapper.class);
        mapper.insertUserPic(userPic);
        sqlSession.commit();
    }
    finally
    {
        sqlSession.close();
    }
}

下面的getUserPic()方法展示了怎样将CLOB类型数据读取到String类型,BLOB类型数据读取成byte[]属性:

public void getUserPic()
{
    UserPic userPic = null;
    SqlSession sqlSession = MyBatisUtil.openSession();
    try
    {
        UserPicMapper mapper =
            sqlSession.getMapper(UserPicMapper.class);
        userPic = mapper.getUserPic(1);
    }
    finally
    {
        sqlSession.close();
    }
    byte[] pic = userPic.getPic();
    try
    {
        OutputStream os = new FileOutputStream(new
                                               File("C:\\Images\\UserImage_FromDB.jpg"));
        os.write(pic);
        os.close();
    }
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
}

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);

<select id=" findAllStudents" resultMap="StudentResult"> select * from Students </select>
Map<Integer, Student> studentMap = 
sqlSession.selectMap("com.mybatis3.mappers.StudentMapper.findAllStudents", "studId");

这里studentMap将会将studId作为key值,而Student对象作为value值。

<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。

对于sqlSession.select()方法,我们可以传递给它一个ResultHandler的实现,它会被调用来处理ResultSet的每一条记录。

public interface ResultHandler
{
    void handleResult(ResultContext context);
}

现在然我们来看一下怎么使用ResultHandler来处理结果集ResultSet,并返回自定义化的结果。

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;
}

     在上述的代码中,我们提供了匿名内部ResultHandler实现类,在handleResult()方法中,我们使用context.getResultObject()获取当前的result对象,即Student对象,因为我们定义了findAllStudent映射语句的resultMap=”studentResult“。对查询返回的每一行都会调用handleResult()方法,并且我们从Student对象中取出studId和name,将其放到map中。

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

以下是对上述属性的描述:

  •         ž eviction:此处定义缓存的移除机制。默认值是LRU,其可能的值有:LRU(least recently used,最近最少使用),FIFO(first infirst out,先进先出),SOFT(soft reference,软引用),WEAK(weak reference,弱引用)。
  •         ž 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 上找到关于继承第三方缓存类库的更多信息。

3.8 总结

     在本章中,我们学习了怎样使用映射器配置文件 书写SQL映射语句。讨论了如何配置简单的语句,一对一以及一对多关系的语句,以及怎样使用ResultMap进行结果集映射。我们还了解了构建动态SQL语句,结果分页,以及自定义结果集(ResultSet)处理。在下一章,我们将会讨论如何使用注解书写映射语句。





前一章:

下一章:Java Persistence with MyBatis 3(中文版) 第四章 使用注解配置SQL映射器


-------------------------------------------------------------------------------------------------------------------------------

作者声明:本文出处是http://blog.csdn.net/luanlouis,如需转载,请注明出处!






Java Persistence with MyBatis 3(中文版) 第三章 使用XML配置SQL映射器,布布扣,bubuko.com

热门排行

今日推荐

热门手游