MyBatis框架核心之(四)Mapper文件使用resultMap及多表查询

四、resultMap与多表查询(mapper.xml文件)

一、resultMap简介

MyBatis是基于“数据库结构不可控”的思想建立的,也就是我们希望数据库遵循第三范式或BCNF,但实际事与愿违,那么结果集映射就是MyBatis为我们提供这种理想与现实间转换的手段了,而resultMap就是结果集映射的配置标签了。

 

1.SQL查询结果到领域模型实体                  

  在深入ResultMap标签前,我们需要了解从SQL查询结果集到JavaBeanPOJO实体的过程。

  1. 通过JDBC查询得到ResultSet对象

  2. 遍历ResultSet对象并将每行数据暂存到HashMap实例中,以结果集的字段名或字段别名为键,以字段值为值

  3. 根据ResultMap标签的type属性通过反射实例化领域模型

  4. 根据ResultMap标签的type属性和idresult等标签信息将HashMap中的键值对,填充到领域模型实例中并返回

 

2.使用场景

       在项目的实际开发中,有可能会遇到这样两种情况。

1.   实体类中的属性名与列名不相同,不能改但。导致不能自动装配值

2.   多表查询的返回值中可能需要其他对象,或者数组(一对一和一对多)

 

二、resultMap标签解释

标签及属性介绍

<resultMap > 标签:

      id属性 resultMap标签的标识。

          type属性 ,返回值的全限定类名,或类型别名。

     autoMapping属性 ,值范围true(默认值)|false, 设置是否启动自动映射功能,自动映射功能就是自动查找与字段名小写同名的属性名,并调用setter方法。而设置为false后,则需要在`resultMap`内明确注明映射关系才会调用对应的setter方法。

 

 

 <resultMap>可以设置的子标签映射:

       1).id标签  :ID 结果,将结果集标记为ID,以方便全局调用(适用于指定主键)

column 数据库的列名

Property需要装配的属性名

      2).result标签:将查询到的列的值,反射到指定的JavaBean的 属性上

column 数据库的列名

Property 需要装配的属性名

      3).association标签:复杂类型  , 多表查询(一对一)时,将根据外键或某列信息查询出的对象,直接装配给某个resultMap指定的属性。

column 数据库的列名

Property 需要装配的属性名

select  指定用来多表查询的sqlmapper

 

      4).Collection标签:复杂类型,多表查询(一对多),将查询出的结果集合直接装配给某个对应的集合

column 数据库的列名

Property 需要装配的属性名

javaType 指定用什么类型接受返回的值(必要)

select  指定用来多表查询的sqlmapper

      5).constructor– 用来将结果反射给一个实例化好的类的构造器

a) idArg –ID 参数;将结果集标记为ID,以方便全局调用
b) arg –反射到构造器的通常结果

      6).discriminator – 使用一个结果值以决定使用哪个resultMap

a) case – 基本一些值的结果映射的case 情形

i. nestedresult mappings –一个case 情形本身就是一个结果映射,因此也可以包括一些相同的元素,也可以引用一个外部resultMap。

 

 

<resultMap type="" id="">

<id column="" property=""/>

<result column="" property="" />

       <association property="" column="" select=""></association>

       <collection property="" column="" javaType="" select=""></collection>

       <constructor></constructor>

    </resultMap>

 

     

 

三、各标签使用

1.id、result

       id、result是最简单的映射,id为主键映射;result其他基本数据库表字段到实体类属性的映射。

实体字段                    表的列名     

sid                            stuid

sname                      stuname

gid                            gid

grade                       grade  

<resultMap type="student" id="studentMap" autoMapping="true">

    <!-- 主键建议用id标签   -->

    <id column="stuid" property="sid"/>

    <!-- 其他列可以用result标签 -->

    <result column="stuname" property="sname"/>

</resultMap>

 

 

id、result语句属性配置细节:

 

属性

描述

 

property

需要映射到JavaBean 的属性名称。

 

column

数据表的列名或者标签别名。

 

javaType

一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。然后,如果你是要映射到一个HashMap,那你需要指定javaType 要达到的目的。

 

jdbcType

数据表支持的类型列表。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果你是直接针对JDBC 编码,且有允许空的列,而你要指定这项。

 

typeHandler

使用这个属性可以覆写类型处理器。这项值可以是一个完整的类名,也可以是一个类型别名。

 

 

支持的JDBC类型
       为了将来的引用,MyBatis支持下列JDBC 类型,通过JdbcType 枚举:
BIT,FLOAT,CHAR,TIMESTAMP,OTHER,UNDEFINED,TINYINT,REAL,VARCHAR,BINARY,BLOB,NVARCHAR,SMALLINT,DOUBLE,LONGVARCHAR,VARBINARY,CLOB,NCHAR,INTEGER,NUMERIC,DATE,LONGVARBINARY,BOOLEAN,NCLOB,BIGINT,DECIMAL,TIME,NULL,CURSOR

 

2.association联合

联合元素用来处理“一对一”的关系

需要指定映射的Java实体类的属性,属性的javaType(通常MyBatis 自己会识别)。对应的数据库表的列名称。如果想覆写的话返回结果的值,需要指定typeHandler。
不同情况需要告诉MyBatis 如何加载一个联合。MyBatis 可以用两种方式加载:

1. select: 执行一个其它映射的SQL 语句返回一个Java实体类型。较灵活;
2. resultsMap: 使用一个嵌套的结果映射来处理通过join查询结果集,映射成Java实体类型。

 

 

 

  association 标签

    property=多表查询装配的属性名

    column=通过那一列关联

    select=指定查询语句,如果查询语句在其他的namespace中,则需要写全namespace.方法id

 

例如:在获取某个学生的信息的同时又想获取所属班级的所有信息

学生实体中的字段

package cn.et.fuqiang.resultMap.entity;

 

public class Student {

     private Integer sid;//学生id

     private String sname; //学生姓名

     private Integer gid; //班级id

 

     private Grade grade; //所属班级的所有信息

}

 

班级实体的字段

package cn.et.fuqiang.resultMap.entity;

 

import java.util.List;

 

public class Grade {

    private Integer id;//班级id

    private String gname;//班级名称

       public Grade() {

       super();

       // TODO Auto-generated constructor stub

    }

   

}

 

 

在Student的字段中,Grade班级实体,需要查询班级表才能获取,学生表中没有该列(所以需要使用多表查询中的 association标签)

想要使用多表查询先要定义一个resultMap,简单来说其实相当于一个工厂,可以指定某个实体中那些属性可以装哪一列的值,并且还要定义类似于子查询的方法。

<!-- 使用resultMap 多表查询 -->

<resultMap type="student" id="gradeInStudent">

    <!-- 主键建议用id标签   -->

    <id column="stuid" property="sid"/>

       

    <!-- 其他列可以用result标签 -->

    <result column="stuname" property="sname"/>

    <!-- 多对一  多表查询

        返回一个班级实体-->

    <association property="grade" column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>

</resultMap>

namespace="cn.et.fuqiang.resultMap.xml.GradeXmlInterface下的方法

<select id="queryGradeById" resultMap="gradeMap">

       select * from grade where gid=#{0}

</select>

在这个resultMap中

type:是上面定义的学生类(使用了别名,如果没有别名可以写全类名)

id:是这个resultMap的标识

因为表中的字段与实体中的不同,所以使用了下面两个子标签,将表的列与实体的属性做了映射。这样才能将表中的值获取到实体的属性中。

<!-- 主键建议用id标签   -->

<id column="stuid" property="sid"/>

       

<!-- 其他列可以用result标签 -->

<result column="stuname" property="sname"/>

因为实体中还要一个班级实体,但表中没有该字段,所以就要用多表查询

<!-- 多对一 多表查询返回一个班级实体-->

<association property="grade"column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>

(这里的property是实体中的字段,而column=gid就相当于关联的条件了

而select就是该关联条件对应的查询方法)

 

主sql

在这里我们使用resultMap定义返回的类型,这样就会自动将所有内容,装到对应的属性上

然后直接使用该主sql,查询,获取的值就会自动对应到其中。

 

<select id="quserAllStudent" resultMap="gradeInStudent">

   select * from student

</select>

 

 

 

以上代码写为标准的sql语句

select  s.*,g.*from student s inner join grade g ons.gid=g.gid

 

 

3.  collection聚集

聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList);列表中对象的类型ofType(Java实体类);对应的数据库表的列名称;
不同情况需要告诉MyBatis 如何加载一个聚集。MyBatis 可以用两种方式加载:

1. select:执行一个其它映射的SQL 语句返回一个Java实体类型。较灵活;
2. resultsMap: 使用一个嵌套的结果映射来处理通过join查询结果集,映射成Java实体类型。

 

 

 

Collection标签

    property=多表查询装配的属性名

    column=通过那一列关联

    select=指定查询语句,如果查询语句在其他的namespace中,则需要写全namespace.方法id

    javaType=java实体中接受的集合类型

 

例如,一个班级有多个学生。

 

学生实体中的字段

package cn.et.fuqiang.resultMap.entity;

 

public class Student {

     private Integer sid;//学生id

     private String sname; //学生姓名

     private Integer gid; //班级id

}

 

班级实体的字段

package cn.et.fuqiang.resultMap.entity;

 

import java.util.List;

 

public class Grade {

    private Integer id;//班级id

    private String gname;//班级名称

      private List<Student> students;//该班级下所有的学生

    public Grade() {

       super();

       // TODO Auto-generated constructor stub

    }

   

}

 

 

在Grade实体中有个List<Student> students属性,需要查询学生表才能获取,班级表中没有该信息(所以要使用collection聚集查询对应的结果集)

 

<!-- 使用resultMap 多表查询 -->

    <resultMap type="grade" id="studentsInGrade">

        <!-- 主键适合用 id 标签 -->

        <id column="gid" property="id" />

        <!-- 一对多     多表查询     collection 可获取一个集合

            property=多表查询装配的属性名

            column=通过那一列关联

            select=查询语句

            javaType=java实体中接受的集合类型

        -->

       

    <collection property="students" column="gid" javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>

    </resultMap>

namespace="cn.et.fuqiang.resultMap.xml.StudentXmlInterface"下的查询语句和类型

<resultMap type="student" id="studentMap" autoMapping="true">

         <!-- 主键建议用id标签   -->

         <id column="stuid" property="sid"/>

         <!-- 其他列可以用result标签 -->

         <result column="stuname" property="sname"/>

</resultMap>

     <!-- 使用多表查询班级   

    注意: resultMap使用时应注意,如果该resultMap中配置了多表查询,如果是互相查询可能造成死循环

       -->

     <select id="quserUserByGid" resultMap="studentMap">

         select * from student where gid=#{0}

     </select>

    

注意:因为实例中学生的字段名与表中的不同所以,又加了一个学生查询专属的resultMap

在这个resultMap中

type:是上面定义的学生类(使用了别名,如果没有别名可以写全类名)

id:是这个resultMap的标识

因为表中的字段与实体中的不同,所以使用了下面的子标签,将表的列与实体的属性做了映射。这样才能将表中的值获取到实体的属性中。

<!-- 主键适合用 id 标签 -->

<id column="gid" property="id"/>

使用多表查询获取某个班级下的所有学生

    

<collection property="students" column="gid"javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>

     </resultMap>

(这里的property是实体中的字段,而column=gid就相当于关联的条件了

而select就是该关联条件对应的查询方法)

 

 

主sql

<select id="queryAllGrade" resultMap="studentsInGrade">

      select * from grade

</select>

执行该映射时就会自动根据resultMap自动调用其中内嵌的sql语句查询所有学生

 

 

 

 

所有代码实例

Mybatis配置

mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <!-- 指定jdbc的配置文件位置 -->

    <properties resource="cn/et/fuqiang/resultMap/jdbc.properties"></properties>

    <!-- 配置类别名 -->

    <typeAliases>

    <!-- 使用该标签将该包下的所有类统一起别名   默认为类名首字母小写           -->

       <package name="cn.et.fuqiang.resultMap.entity"/>

    </typeAliases>

    <!-- 配置jdbc环境 -->

    <environments default="development">

       <environment id="development">

           <transactionManager type="JDBC" />

           <!-- 配置数据库连接信息 -->

           <dataSource type="POOLED">

              <property name="driver" value="${driverClass}" />

              <property name="url" value="${url}" />

              <property name="username" value="${user}" />

              <property name="password" value="${password}" />

           </dataSource>

       </environment>

    </environments>

    <!-- 使用接口映射  配置查询语句 -->

    <mappers>

    <!-- 注册接口   如果使用的是注解则不需要mappers文件-->

       <!-- 有两种方式配置mapper

       第一种  如果有mapper.xml文件直接配置该文件

       <mapper resource="mapper 文件的类路径">

       第二种  配置接口的全类名

       <mapper class="接口的全类名">

       -->

       <mapper class="cn.et.fuqiang.resultMap.xml.StudentXmlInterface"/>

       <mapper class="cn.et.fuqiang.resultMap.xml.GradeXmlInterface"/>

    </mappers>

 

</configuration>

 

Jdbc连接四要素配置

jdbc.properties

url=jdbc:oracle:thin:@localhost:1521:orcl

user=mybatis

password=mybatis

driverClass=oracle.jdbc.OracleDriver

两个实体

student实体

package cn.et.fuqiang.resultMap.entity;

 

public class Student {

    private Integer sid;//学生id

    private String sname;//学生姓名

    private Integer gid;//班级id

    private Grade grade;//所属班级的所有信息

    public Student() {

        super();

        // TODO Auto-generated constructor stub

    }

    public Student(Integer sid, String sname, Integer gid, Grade grade) {

        super();

        this.sid = sid;

        this.sname = sname;

        this.gid = gid;

        this.grade = grade;

    }

    public Integer getSid() {

        return sid;

    }

    public void setSid(Integer sid) {

        this.sid = sid;

    }

    public String getSname() {

        return sname;

    }

    public void setSname(String sname) {

        this.sname = sname;

    }

    public Integer getGid() {

        return gid;

    }

    public void setGid(Integer gid) {

        this.gid = gid;

    }

    public Grade getGrade() {

        return grade;

    }

    public void setGrade(Grade grade) {

        this.grade = grade;

    }

}

 

Grade实体

package cn.et.fuqiang.resultMap.entity;

 

import java.util.List;

 

public class Grade {

    private Integer id;//班级

    private String gname;//班级名称

    private List<Student> students;//该班级下所有的学生

    public Grade() {

        super();

        // TODO Auto-generated constructor stub

    }

    public Grade(Integer id, String gname) {

        super();

        this.id = id;

        this.gname = gname;

    }

   

    public String getGname() {

        return gname;

    }

    public void setGname(String gname) {

        this.gname = gname;

    }

    public Integer getId() {

        return id;

    }

    public void setId(Integer id) {

        this.id = id;

    }

    public List<Student> getStudents() {

        return students;

    }

    public void setStudents(List<Student> students) {

        this.students = students;

    }

   

}

 

 

两个接口映射的接口

GradeXmlInterface.java

package cn.et.fuqiang.resultMap.xml;

 

import java.util.List;

 

import cn.et.fuqiang.resultMap.entity.Grade;

 

public interface GradeXmlInterface {

    public Grade queryGradeById(Integer id);

    public List<Grade> queryAllGrade();

}

 

StudentXmlInterface.java

package cn.et.fuqiang.resultMap.xml;

 

import java.util.List;

 

import cn.et.fuqiang.resultMap.entity.Student;

 

public interface StudentXmlInterface {

    public Student queryStudentById(Integer sid);

    public List<Student> quserAllStudent();

    public List<Student> quserUserByGid();

}

 

 

两个映射的mapper.xml

GradeXmlInterface.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.et.fuqiang.resultMap.xml.GradeXmlInterface">

    <!-- 当实体的属性名与表名不一致时可使用 resultMap标签指定对应的列值给对应的属性

       autoMapping是设置是否自动装配属性名与表的列名对应的变量

    -->

    <resultMap type="grade" id="gradeMap" autoMapping="false">

       <!-- 主键适合用 id 标签 -->

       <id column="gid" property="id"/>

       <!-- 其他的用result标签 -->

       <result column="gname" property="gname"/>

    </resultMap>

    <select id="queryGradeById" resultMap="gradeMap">

       select * from grade where gid=#{0}

    </select>

   

    <!-- 使用resultMap 多表查询 -->

    <resultMap type="grade" id="studentsInGrade">

       <!-- 主键适合用 id 标签 -->

       <id column="gid" property="id" />

       <!-- 一对多     多表查询     collection 可获取一个集合

           property=多表查询装配的属性名

           column=通过那一列关联

           select=查询语句

           javaType=java实体中接受的集合类型

       -->

      

       <collection property="students" column="gid" javaType="list" select="cn.et.fuqiang.resultMap.xml.StudentXmlInterface.quserUserByGid"></collection>

    </resultMap>

 

       <!-- 使用多表查询在查询班级的同时根据上面的resultMap查出对应的所有学生 

       注意: resultMap使用时应注意,如果该resultMap中配置了多表查询,如果是互相查询可能造成死循环

      -->

    <select id="queryAllGrade" resultMap="studentsInGrade">

       select * from grade

    </select>

   

 

</mapper>

StudentXmlInterface.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="cn.et.fuqiang.resultMap.xml.StudentXmlInterface">

    <!-- 当实体的属性名与表名不一致时可使用 resultMap标签指定对应的列值给对应的属性

       autoMapping是设置是否自动装配属性名与表的列名对应的变量(不写默认是true)

    -->

    <resultMap type="student" id="studentMap" autoMapping="true">

       <!-- 主键建议用id标签   -->

       <id column="stuid" property="sid"/>

       <!-- 其他列可以用result标签 -->

       <result column="stuname" property="sname"/>

    </resultMap>

    <select id="queryStudentById" resultMap="studentMap">

       select * from student where stuid=#{0}

    </select>

    <!-- 使用resultMap 多表查询 -->

    <resultMap type="student" id="gradeInStudent">

       <!-- 主键建议用id标签   -->

       <id column="stuid" property="sid"/>

      

       <!-- 其他列可以用result标签 -->

       <result column="stuname" property="sname"/>

       <!-- 多对一     多表查询     association

           property=多表查询装配的属性名

           column=通过那一列关联

           select=查询语句

       -->

       <association property="grade" column="gid" select="cn.et.fuqiang.resultMap.xml.GradeXmlInterface.queryGradeById"></association>

    </resultMap>

    <select id="quserAllStudent" resultMap="gradeInStudent">

       select * from student

    </select>

   

   

    <!-- 使用多表查询班级   

       注意: resultMap使用时应注意,如果该resultMap中配置了多表查询,如果是互相查询可能造成死循环

      -->

    <select id="quserUserByGid" resultMap="studentMap">

       select * from student where gid=#{0}

    </select>

   

</mapper>

 

 

测试类

package cn.et.fuqiang.resultMap.xml;

 

import java.io.InputStream;

import java.util.List;

 

import org.apache.ibatis.session.SqlSession;

import org.apache.ibatis.session.SqlSessionFactory;

import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import org.junit.Test;

 

import cn.et.fuqiang.resultMap.entity.Grade;

import cn.et.fuqiang.resultMap.entity.Student;

 

 

public class ResultMapXmlTest {

   private static SqlSession session;

   private static GradeXmlInterface gradeInter=null;

   private static StudentXmlInterface studentInter=null;

   static{

       //mybatis的配置文件

       String resource = "mybatis.xml";

       //使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)

       InputStream is =GradeXmlInterface.class.getResourceAsStream(resource);

       //构建sqlSession的工厂

       SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);

       //使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件)

       //Readerreader = Resources.getResourceAsReader(resource);

       //构建sqlSession的工厂

       //SqlSessionFactorysessionFactory = new SqlSessionFactoryBuilder().build(reader);

       //创建能执行映射文件中sqlsqlSession

       session = sessionFactory.openSession();

       gradeInter=session.getMapper(GradeXmlInterface.class);

       studentInter=session.getMapper(StudentXmlInterface.class);

   }

   //@Test

   public void queryGradeTest(){

       Grade grade=gradeInter.queryGradeById(1);

       System.out.println(grade.getId()+"------"+grade.getGname());

   }

   //@Test

   public void queryStudentTest(){

       Student student=studentInter.queryStudentById(1);

       System.out.println(student.getSid()+"------"+student.getSname());

   }

   //@Test

   public void queryGradeInStudent(){

       List<Student> students=studentInter.quserAllStudent();

       for (Student stu : students) {

            System.out.println(stu.getSid()+"------"+stu.getSname()+"-------"+stu.getGrade().getGname());

       }

       

   }

   @Test

   public void queryStudentInGrade(){

       List<Grade> students=gradeInter.queryAllGrade();

       for (Grade grade : students) {

            System.out.println(grade.getGname()+""+grade.getStudents().size()+"");

       }

   }

}

 

  • 18
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值