MyBatis学习总结(七):MyBatis的一对一查询

一、<resultMap> 元素

<resultMap> 元素表示结果映射集,是 MyBatis 中最重要也是最强大的元素,主要用来定义映射规则、级联的更新以及定义类型转化器等。其基本结构如下:

<resultMap id="" type="">
    <constructor><!-- 类再实例化时用来注入结果到构造方法 -->
        <idArg/><!-- ID参数,结果为ID -->
        <arg/><!-- 注入到构造方法的一个普通结果 -->  
    </constructor>
    <id/><!-- 用于表示哪个列是主键 -->
    <result/><!-- 注入到字段或JavaBean属性的普通结果 -->
    <association property=""/><!-- 用于一对一关联 -->
    <collection property=""/><!-- 用于一对多、多对多关联 -->
    <discriminator javaType=""><!-- 使用结果值来决定使用哪个结果映射 -->
        <case value=""/><!-- 基于某些值的结果映射 -->
    </discriminator>
</resultMap>

<resultMap> 元素与<resultType>元素的区别:

相同点:都是用于select语句的返回类型。

不同:

resultType 是直接表示返回类型的(一般返回为基本数据类型时使用,当查询的是一条SQL数据时,且这条SQL数据的每个字段都和一个Javabean 中的属性名 与之对应,mybatis 会通过 autoMapping ,将每个字段的值赋给 Javabean),而当字段名和属性名不一致时,这时可以使用 resultMap 

resultMap 是对外部的一个 ResultMap 标签的引用,并且  resultType 跟 resultMap 不能同时存在。

resultMap的基本使用:

    <resultMap type="com.day1.entity.Student" id="stuMap">
        <result column="username" property="username"/>
        <result column="age" property="age"/>
        <result column="address" property="address"/>
    </resultMap>
    <select id="selectStudent" resultMap="stuMap" parameterType="int">
        select * from t_student where id=#{id}
    </select>

当字段名和属性名不一致时,可以使用 resultMap 。假设Student类中的属性名分别为:s_name、s_age、s_address,则上述代码应修改为:

    <resultMap type="com.day1.entity.Student" id="stuMap">
        <result column="username" property="s_name"/>
        <result column="age" property="s_age"/>
        <result column="address" property="s_address"/>
    </resultMap>
    <select id="selectStudent" resultMap="stuMap" parameterType="int">
        select * from t_student where id=#{id}
    </select>

二、一对一关联查询

一对一关系在现实生活中是十分常见的,例如一个人只有一个身份证、一个员工只属于一个部门。在 MyBatis 中,通过 <resultMap> 元素的子元素 <association> 处理这种一对一级联关系。

在 <association> 元素中通常使用以下属性。

  • property:指定映射到实体类的对象属性。
  • column:指定表中对应的字段(即查询返回的列名)。
  • javaType:指定映射到实体对象属性的类型。
  • select:指定引入嵌套查询的子 SQL 语句,该属性用于关联映射中的嵌套查询。

一对一关联有三种方式:

(1)嵌套查询,执行两个SQL语句

(2)嵌套结果,执行一个SQL语句

(3)连接查询,使用POJO存储结果

接下来就对上述三种方式进行演示。

方式一:嵌套查询,执行两个SQL语句

(1)创建部门表和员工表

CREATE TABLE `t_dept` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `dept_name` varchar(20) DEFAULT NULL,
  `dept_desc` varchar(100) DEFAULT NULL,
  PRIMARY KEY (`id`)
);
insert into t_dept(dept_name, dept_age) values("开发部","软件开发");
insert into t_dept(dept_name, dept_age) values("测试部","软件测试");
insert into t_dept(dept_name, dept_age) values("财务部","发放钱财");
insert into t_dept(dept_name, dept_age) values("人事部","招聘员工");

CREATE TABLE `t_emp` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `emp_name` varchar(20) DEFAULT NULL,
  `emp_age` int(11) DEFAULT NULL,
  `dept_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `dept_id` (`dept_id`),
  CONSTRAINT `t_emp_ibfk_1` FOREIGN KEY (`dept_id`) REFERENCES `t_dept` (`id`)
);
INSERT INTO `t_emp` VALUES (1,'张倩',22,2),(2,'杨帆',24,1),(3,'章飞',21,3),(4,'李冰',51,4),(5,'王猛',36,3),(6,'康生',26,1),(7,'刘婷',20,3),(8,'刘羽',22,2);

 (2) 创建实体类部门类Dept和员工类Emp。

部门类Dept:

public class Dept {
    private Integer id;
    private String deptName;
    private String deptDesc;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    public String getDeptDesc() {
        return deptDesc;
    }

    public void setDeptDesc(String deptDesc) {
        this.deptDesc = deptDesc;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "id=" + id +
                ", deptName='" + deptName + '\'' +
                ", deptDesc='" + deptDesc + '\'' +
                '}';
    }
}

员工类Emp:

public class Emp {
    private Integer id;
    private String empName;
    private String empAge;
    private Dept dept;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpAge() {
        return empAge;
    }

    public void setEmpAge(String empAge) {
        this.empAge = empAge;
    }

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "id=" + id +
                ", empName='" + empName + '\'' +
                ", empAge='" + empAge + '\'' +
                ", dept=" + dept +
                '}';
    }
}

(3)创建IDeptDao和IEmpDao

IDeptDao.java:

public interface IDeptDao {
    /**
     * 根据id查询部门信息
     * @param id id值
     * @return
     */
    Dept selectDeptById(int id);
}

IEmpDao.java文件: 

public interface IEmpDao {
    /**
     * 根据id查询员工信息
     * @param id id值
     * @return
     */
    Emp selectEmpById(int id);
}

(4)创建deptMapper.xml和empMapper.xml文件

deptMapper.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">
<!--namespace:指定了唯一的命名空间-->
<mapper namespace="com.day1.dao.IDeptDao">
    <!--    这里使用resultMap是为了解决数据库列名和实体类属性名不一致的问题    -->
    <resultMap type="com.day1.entity.Dept" id="deptProps">
        <id property="id" column="id"/>
        <result property="deptName" column="dept_name"/>
        <result property="deptDesc" column="dept_desc"/>
    </resultMap>
    <select id="selectDeptById" parameterType="int"  resultMap="deptProps">
        select * from t_dept where id = #{id}
    </select>

</mapper>

empMapper.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">
<!--namespace:指定了唯一的命名空间-->
<mapper namespace="com.day1.dao.IEmpDao">
    <resultMap type="com.day1.entity.Emp" id="empAndDept">
        <id property="id" column="id"/>
        <result property="empName" column="emp_name"/>
        <result property="empAge" column="emp_age"/>
        <!-- 一对一级联查询-->
        <association property="dept" column="dept_id" javaType="com.day1.entity.Dept"
                     select="com.day1.dao.IDeptDao.selectDeptById"/>
    </resultMap>
    <select id="selectEmpById" parameterType="int" resultMap="empAndDept">
        select * from t_emp where id = #{id}
    </select>

</mapper>

(5)在SqlMapperConfig.xml中配置mapper文件

    <!--  指定映射的配置文件位置:每一个都是独立的文件  -->
    <mappers>
        <mapper resource="com/day1/deptMapper.xml"></mapper>
        <mapper resource="com/day1/empMapper.xml"></mapper>
    </mappers>

(6)进行测试。

    @Test
    public void testSelect01() throws IOException {
        //1、读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapperConfig.xml");
        //2、创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3、使用工厂生产SqlSession对象
        SqlSession sqlSession = factory.openSession();
        //4、使用SqlSession创建dao接口的代理对象
        IEmpDao empDao = sqlSession.getMapper(IEmpDao.class);
        //5、使用代理对象执行方案
        Emp emp = empDao.selectEmpById(2);
        System.out.println(emp);
        //6、释放资源
        sqlSession.close();
        in.close();
    }

方式二:嵌套结果,执行一个SQL语句

方式二不需要使用deptMapper.xml文件中的sql语句,而是在empMapper.xml文件中通过一条sql语句完成查询操作。修改上述查询语句如下:

<resultMap type="com.day1.entity.Emp" id="empAndDept">
        <id property="id" column="id"/>
        <result property="empName" column="emp_name"/>
        <result property="empAge" column="emp_age"/>
        <!-- 一对一级联查询-->
        <association property="dept" javaType="com.day1.entity.Dept">
            <!--      此处id代表的是dept的id      -->
            <id property="id" column="id"/>
            <result property="deptName" column="dept_name"/>
            <result property="deptDesc" column="dept_desc"/>
        </association>
    </resultMap>
    <select id="selectEmpById" parameterType="int" resultMap="empAndDept">
        select e.*, d.dept_name, d.dept_desc from t_emp e, t_dept d where e.id = #{id} and e.dept_id = d.id;
    </select>

方式三:连接查询,使用POJO存储结果

方式三需要提供一个Emp的扩展类,如下:

public class EmpExtend{
    private Integer id;
    private String empName;
    private String empAge;
    private String deptName;
    private String deptDesc;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public String getEmpAge() {
        return empAge;
    }

    public void setEmpAge(String empAge) {
        this.empAge = empAge;
    }

    public String getDeptName() {
        return deptName;
    }

    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }

    public String getDeptDesc() {
        return deptDesc;
    }

    public void setDeptDesc(String deptDesc) {
        this.deptDesc = deptDesc;
    }

    @Override
    public String toString() {
        return "EmpExtend{" +
                "id=" + id +
                ", empName='" + empName + '\'' +
                ", empAge='" + empAge + '\'' +
                ", deptName='" + deptName + '\'' +
                ", deptDesc='" + deptDesc + '\'' +
                '}';
    }
}

修改IEmpDao的方法返回值如下:

EmpExtend selectEmpById(int id);

修改empMapper.xml中的映射语句如下:

       <resultMap type="com.day1.entity.EmpExtend" id="empExtend">
            <id property="id" column="id"/>
            <result property="empName" column="emp_name"/>
            <result property="empAge" column="emp_age"/>
            <result property="deptName" column="dept_name"/>
            <result property="deptDesc" column="dept_desc"/>
        </resultMap>
        <select id="selectEmpById" parameterType="int" resultMap="empExtend">
            select e.*, d.dept_name, d.dept_desc from t_emp e, t_dept d where e.id = #{id} and e.dept_id = d.id;
        </select>

修改测试方法如下:

    @Test
    public void testSelect01() throws IOException {
        //1、读取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapperConfig.xml");
        //2、创建SqlSessionFactory工厂
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3、使用工厂生产SqlSession对象
        SqlSession sqlSession = factory.openSession();
        //4、使用SqlSession创建dao接口的代理对象
        IEmpDao empDao = sqlSession.getMapper(IEmpDao.class);
        //5、使用代理对象执行方案
        EmpExtend empExtend = empDao.selectEmpById(2);
        System.out.println(empExtend);
        //6、释放资源
        sqlSession.close();
        in.close();
    }

三种方式的对比:

方式一采用了分文件的方式执行了两次sql语句,可重用性比较好。

方式二在一个mapper文件中就可以完成查询操作,执行了一次sql语句。

方式三尽管可以实现功能,但是需要提供扩展类,会出现大量的冗余代码,造成开发的复杂度增大和可重用性降低。

猜你喜欢

转载自blog.csdn.net/weixin_47382783/article/details/113830978
今日推荐