mybatis關聯查詢之一對多查詢


一對多,是最常見的一種設計。就是 A 表的一條記錄,對應 B 表的多條記錄,且 A 的主鍵作為 B 表的外鍵。這主要看以哪張表為中心,下面的測試數據中,從employee 表來看,一個員工對應一個部門,是一對一關系,如果從部門角度來看,則是一對多的關系,一個部門對應多個員工,本節主要研究一對多的關系。

查詢部門的時候將部門對應的所有員工信息也查詢出來

數據表建立


新建數據表department,有兩個字段,插入兩條數據如下:

id dept_name
1 CIA
2 FSB

新建數據表employee,有三個字段,其中dept_id是外鍵,關聯department表的主鍵id。插入數據如下:

id last_name dept_id
1 Tom 1
2 Jerry 2
3 Neo 1
4 Cypher 2

新建maven工程,添加依賴,主要是mybatis和mysql


<dependencies>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.6</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.47</version>
    </dependency>
</dependencies>

編寫數據庫表對應的實體。


對於department表,對應實體如下:注意增加一個包含了Employee集合。

package com.yefengyu.mybatis.entity;

import java.util.List;


public class Department
{
    private Integer id;

    private String deptName;

    private List<Employee> employees;

    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 List<Employee> getEmployees()
    {
        return employees;
    }

    public void setEmployees(List<Employee> employees)
    {
        this.employees = employees;
    }

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

對於employee表,實體如下,注意在Employee實體中把外鍵直接變成對於Department對象的引用。

package com.yefengyu.mybatis.entity;

public class Employee
{
    private Integer id;

    private String lastName;

    private Department department;

    public Integer getId()
    {
        return id;
    }

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

    public String getLastName()
    {
        return lastName;
    }

    public void setLastName(String lastName)
    {
        this.lastName = lastName;
    }

    public Department getDepartment()
    {
        return department;
    }

    public void setDepartment(Department department)
    {
        this.department = department;
    }

    @Override
    public String toString()
    {
        return "Employee{" +
               "id=" + id +
               ", lastName='" + lastName + '\'' +
               ", department=" + department +
               '}';
    }
}

編寫mapper接口


package com.yefengyu.mybatis.mapper;

import com.yefengyu.mybatis.entity.Department;


public interface DepartmentMapper
{
    public Department getDeptById(Integer id);
}

根據部門ID查詢部門信息和對應的所有員工信息。

編寫mapper映射文件(本節重點


collection嵌套結果集方法:

<?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.yefengyu.mybatis.mapper.DepartmentMapper">
    <resultMap id="dept" type="com.yefengyu.mybatis.entity.Department">
        <id column="d_id" property="id"/>
        <result column="dept_name" property="deptName"/>
        <!--
         collection定義關聯集合類型的屬性的封裝規則
           ofType:指定集合里面元素的類型
          -->
        <collection property="employees" ofType="com.yefengyu.mybatis.entity.Employee" javaType="java.util.ArrayList">
            <id column="id" property="id"/>
            <result column="last_name" property="lastName"/>
        </collection>
    </resultMap>
    <select id="getDeptById" resultMap="dept">
        select e.id id, e.last_name last_name, e.dept_id dept_id, d.id d_id,d.dept_name dept_name
        from department d
        left join employee e
        on e.dept_id = d.id
        where d.id = #{id} 
    </select>
</mapper>

新建一個mybatis全局配置文件,詳細信息見官網


<?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>
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis?characterEncoding=utf8&amp;allowMultiQueries=true"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/DepartmentMapper.xml"/>
    </mappers>
</configuration>

測試


public static void main(String[] args)
    throws IOException
{
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();
    try
    {
        DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
        Department dept = mapper.getDeptById(1);
        System.out.println(dept);
    }
    finally
    {
        sqlSession.close();
    }
}

結果如下:

Created connection 1938056729.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619]
==>  Preparing: select e.id id, e.last_name last_name, e.dept_id dept_id, d.id d_id,d.dept_name dept_name from department d left join employee e on e.dept_id = d.id where d.id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, last_name, dept_id, d_id, dept_name
<==        Row: 1, Tom, 1, 1, CIA
<==        Row: 3, Neo, 1, 1, CIA
<==      Total: 2
Department{id=1, deptName='CIA', employees=[Employee{id=1, lastName='Tom', department=null}, Employee{id=3, lastName='Neo', department=null}]}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@73846619]

上面查詢雖然可以查詢出數據,但是數據過多則會有性能問題,因此好的做法是分步查詢。

分步查詢


1、新增查詢員工的接口,特別注意是根據部門id來查詢

package com.yefengyu.mybatis.mapper;

import com.yefengyu.mybatis.entity.Employee;


public interface EmployeeMapper
{
    Employee getEmpByDeptId(Integer deptId);
}

2、編寫對應的mapper映射文件

<?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.yefengyu.mybatis.mapper.EmployeeMapper">
    <select id="getEmployee" resultType="com.yefengyu.mybatis.entity.Employee">
        select * from employee where dept_id = #{id}
    </select>
</mapper>

3、編寫查詢部門的接口

package com.yefengyu.mybatis.mapper;

import com.yefengyu.mybatis.entity.Department;


public interface DepartmentMapper
{
    public Department getDeptById(Integer id);
}

4、編寫對應的映射文件

<?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.yefengyu.mybatis.mapper.DepartmentMapper">
    <resultMap id="dept" type="com.yefengyu.mybatis.entity.Department">
        <id column="id" property="id"/>
        <result column="dept_name" property="deptName"/>
        <!-- 擴展:多列的值傳遞過去:
               將多列的值封裝map傳遞:column="{key1=column1,key2=column2}"
             fetchType="lazy":表示使用延遲加載;
                      - lazy:延遲
                      - eager:立即
        -->
        <collection property="employees" select="com.yefengyu.mybatis.mapper.EmployeeMapper.getEmployee"
                    column="id" fetchType="lazy">
        </collection>
    </resultMap>
    <select id="getDeptById" resultMap="dept">
        select id ,dept_name from department where id = #{id}
    </select>
</mapper>

5、測試結果

Created connection 1694556038.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
==>  Preparing: select id ,dept_name from department where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, dept_name
<==        Row: 1, CIA
<==      Total: 1
==>  Preparing: select * from employee where dept_id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, last_name, dept_id
<==        Row: 1, Tom, 1
<==        Row: 3, Neo, 1
<==      Total: 2
Department{id=1, deptName='CIA', employees=[Employee{id=1, lastName='Tom', department=null}, Employee{id=3, lastName='Neo', department=null}]}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]

如果測試代碼的打印改為:

System.out.println(dept.getDeptName());

那么結果如下,不會查詢員工信息。

Created connection 1694556038.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
==>  Preparing: select id ,dept_name from department where id = ? 
==> Parameters: 1(Integer)
<==    Columns: id, dept_name
<==        Row: 1, CIA
<==      Total: 1
CIA
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@6500df86]
Returned connection 1694556038 to pool.

全局配置與局部配置


1、全局配置

lazyLoadingEnabled:延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。
默認值:false

aggressiveLazyLoading:當開啟時,任何方法的調用都會加載該對象的所有屬性。 否則,每個屬性會按需加載(參考 lazyLoadTriggerMethods)。
默認值:false (在 3.4.1 及之前的版本默認值為 true),現在新版本可以不用關注此設置。

<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>

2、局部配置fetchType

<association property="" fetchType="eager"></association>
<collection property="" fetchType="lazy"></collection>
 
  • lazy:延遲
  • eager:立即

3、區別(查詢部門信息,不查看員工信息時)

全局 局部 是否延遲
不開啟 不開啟
不開啟 lazy
不開啟 eager
開啟 不開啟
開啟 lazy
開啟 eager


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM