【Mybatis】使用结果嵌套方式完成一对多映射


【一对多的重要意义】

以公司与雇员为例,如果不用一对多,至少需要访问DB两次,一次按取公司的相关属性,第二次取公司下的雇员们,而采用一对多后,你会发现一次DB访问就够了。

例程下载:https://files.cnblogs.com/files/heyang78/MybatisCli220527.rar?t=1653593808

【目的】

完成一个公司Company包含多个雇员Emp的一对多映射。

【窍门】

理清结果集与实体类的映射关系,分清哪些会分类汇总,哪些会进行组内聚合,进而理解MyBatis在背后帮了什么忙。 

【建表充值部分】

create table company(
    id number(10),
    name nvarchar2(20),
    primary key(id)
);

insert into company(id,name) values(1,'google');
insert into company(id,name) values(2,'microsoft');

create table emp(
    id number(10),
    cid number(10),
    name nvarchar2(20),
    primary key(id)
);

insert into emp(id,cid,name) values(1,1,'Andy');
insert into emp(id,cid,name) values(2,1,'Bill');
insert into emp(id,cid,name) values(3,2,'Cindy');
insert into emp(id,cid,name) values(4,2,'Douglas');

【实体类部分】

Company类:

package com.hy.entity;

import java.util.List;

public class Company {
    private long id;
    private String name;
    private List emps;

    public String toString(){
        StringBuilder sb=new StringBuilder();

        sb.append("Company id="+id);
        sb.append(" name="+name);

        for(Emp emp:emps){
            sb.append(" emp="+emp.toString());
        }

        return sb.toString();
    }

    public long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List getEmps() {
        return emps;
    }

    public void setEmps(List emps) {
        this.emps = emps;
    }
}

Emp类:

package com.hy.entity;

public class Emp {
    private long id;
    private String name;

    public String toString(){
        return String.format("Emp id=%d name=%s",id,name);
    }

    public long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

【Mapper接口】

package com.hy.dao;

import com.hy.entity.Company;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface CompanyMapper {
    Company fetchOne(long id);
}

【Mapper.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="com.hy.dao.CompanyMapper">
    <select id="fetchOne" resultMap="map1">
        select c.id as cid,c.name as cname,e.id as eid,e.name as ename
        from company c,emp e
        where c.id=e.cid
        and c.id=#{id}
    select>

    <resultMap id="map1" type="com.hy.entity.Company">
        <result column="cid" property="id"/>
        <result column="cname" property="name"/>

        <collection property="emps" ofType="com.hy.entity.Emp">
            <result column="eid" property="id"/>
            <result column="ename" property="name"/>
        collection>
    resultMap>
mapper>

resultMap顾名思义是结果集的映射,当id=1时SQL语句是:

select c.id as cid,c.name as cname,e.id as eid,e.name as ename
from company c,emp e
where c.id=e.cid
and c.id=1

而出来的结果集是:

SQL> select c.id as cid,c.name as cname,e.id as eid,e.name as ename
  2  from company c,emp e
  3  where c.id=e.cid
  4  and c.id=1;

       CID CNAME                       EID ENAME
---------- -------------------- ---------- --------------------
         1 google                        1 Andy
         1 google                        2 Bill

对照一下Map1里写的,cid给Company的id,cname给Company的name,EID给Emp的id,ename给Emp的name。

对于这样两行的结果集怎么变成一个Company的实例的呢?

我的理解是Mybatis背后 对于同等值就相当于进行group by分类汇总,而emps部分的处理相当于做了listagg组内聚合。

【启动程序】

package com.hy;

import com.hy.dao.CompanyMapper;
import com.hy.entity.Company;
import com.hy.entity.Emp;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.io.InputStream;
import java.util.List;

@SpringBootApplication
public class MyApplication  implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory =
                new SqlSessionFactoryBuilder().build(inputStream);

        try (SqlSession session = sqlSessionFactory.openSession()) {
            CompanyMapper mapper = session.getMapper(CompanyMapper.class);

            Company google= mapper.fetchOne(1);
            System.out.println(google);

            Company microsoft= mapper.fetchOne(2);
            System.out.println(microsoft);
        }
    }
}

【执行结果】

Company id=1 name=google emp=Emp id=1 name=Andy emp=Emp id=2 name=Bill
Company id=2 name=microsoft emp=Emp id=3 name=Cindy emp=Emp id=4 name=Douglas

【参考文档】

https://blog.csdn.net/qq_45173404/article/details/107672660

END

相关