最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

MyBatis一對(duì)多關(guān)系映射方式小結(jié)

 更新時(shí)間:2025年10月11日 09:52:44   作者:Iris761  
在實(shí)際開(kāi)發(fā)中經(jīng)常會(huì)有一對(duì)多的表關(guān)系,因此掌握使用MyBatis處理一對(duì)多關(guān)系映射方式時(shí)很有必要的,本文就來(lái)介紹一下MyBatis一對(duì)多關(guān)系映射方式,感興趣的可以了解一下

1. MyBatis N+1 問(wèn)題詳解

1.1 什么是N+1問(wèn)題

N+1問(wèn)題是指執(zhí)行1次主查詢(xún)獲取N條主記錄,然后對(duì)每條主記錄再執(zhí)行1次關(guān)聯(lián)查詢(xún),總共執(zhí)行 1 + N 次查詢(xún)的性能問(wèn)題。

1.2 示例場(chǎng)景

假設(shè)有:部門(mén)表(department)和員工表(employee),一個(gè)部門(mén)有多個(gè)員工。

產(chǎn)生N+1問(wèn)題的代碼如下:

<!-- 1. 先查詢(xún)所有部門(mén) -->
<select id="selectAllDepartments" resultMap="DepartmentResultMap">
    SELECT id, name FROM department
</select>

<!-- 2. 為每個(gè)部門(mén)查詢(xún)員工 -->
<select id="selectEmployeesByDeptId" resultType="Employee">
    SELECT id, name FROM employee WHERE dept_id = #{deptId}
</select>

<!-- 3. 結(jié)果映射中使用嵌套查詢(xún) -->
<resultMap id="DepartmentResultMap" type="Department">
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <collection property="employees" column="id" 
                ofType="Employee" select="selectEmployeesByDeptId"/>
</resultMap>

執(zhí)行過(guò)程:

List<Department> departments = departmentMapper.selectAllDepartments();
// 實(shí)際執(zhí)行的SQL:
// 1. SELECT id, name FROM department; (假設(shè)返回3個(gè)部門(mén))
// 2. SELECT id, name FROM employee WHERE dept_id = 1;
// 3. SELECT id, name FROM employee WHERE dept_id = 2;
// 4. SELECT id, name FROM employee WHERE dept_id = 3;
// 總共執(zhí)行了 1 + 3 = 4 次查詢(xún)

1.3 產(chǎn)生問(wèn)題

  1. 性能低下:查詢(xún)次數(shù)隨數(shù)據(jù)量線(xiàn)性增長(zhǎng)
  2. 數(shù)據(jù)庫(kù)壓力大:頻繁建立數(shù)據(jù)庫(kù)連接
  3. 響應(yīng)時(shí)間長(zhǎng):網(wǎng)絡(luò)往返次數(shù)多

接下來(lái)講解解決1+N問(wèn)題的方式

2. 使用JOIN 查詢(xún) + Collection 映射

2.1 實(shí)體類(lèi)定義

// 部門(mén)實(shí)體
@Getter
@Setter
public class Department {
    private Long id;
    private String name;
    private List<Employee> employees; // 一對(duì)多關(guān)系
}

// 員工實(shí)體
@Getter
@Setter
public class Employee {
    private Long id;
    private String name;
    private String position;
    private Long deptId;
}

2.2 XML配置

由于 JOIN 查詢(xún)有些情況會(huì)產(chǎn)生重復(fù)的部門(mén)數(shù)據(jù),MyBatis 會(huì)自動(dòng)處理這種重復(fù),但需要使用<id>標(biāo)簽指定好主鍵:

<!-- 使用 JOIN 查詢(xún)一次性獲取所有數(shù)據(jù) -->
<resultMap id="DepartmentWithEmployeesMap" type="Department">
    <id property="id" column="dept_id"/><!-- 重要配置 -->
    <result property="name" column="dept_name"/>
    
    <!-- 使用 collection 映射一對(duì)多關(guān)系 -->
    <collection property="employees" ofType="Employee" javaType="java.util.ArrayList">
        <id property="id" column="emp_id"/><!-- 重要配置 -->
        <result property="name" column="emp_name"/>
        <result property="position" column="position"/>
        <result property="deptId" column="dept_id"/>
    </collection>
</resultMap>

<select id="selectDepartmentWithEmployees" resultMap="DepartmentWithEmployeesMap">
    SELECT 
        d.id as dept_id,
        d.name as dept_name,
        e.id as emp_id,
        e.name as emp_name,
        e.position,
        e.dept_id
    FROM department d
    LEFT JOIN employee e ON d.id = e.dept_id
    WHERE d.id = #{id}
</select>

<!-- 查詢(xún)多個(gè)部門(mén)及其員工 -->
<select id="selectAllDepartmentsWithEmployees" resultMap="DepartmentWithEmployeesMap">
    SELECT 
        d.id as dept_id,
        d.name as dept_name,
        e.id as emp_id,
        e.name as emp_name,
        e.position,
        e.dept_id
    FROM department d
    LEFT JOIN employee e ON d.id = e.dept_id
    ORDER BY d.id, e.id
</select>

2.3 使用實(shí)例

@Service
public class DepartmentService {
    
    @Autowired
    private DepartmentMapper departmentMapper;
    
    // 一次性獲取部門(mén)及其所有員工,避免N+1問(wèn)題
    public Department getDepartmentWithEmployees(Long deptId) {
        return departmentMapper.selectDepartmentWithEmployees(deptId);
    }
    
    // 獲取所有部門(mén)及其員工
    public List<Department> getAllDepartmentsWithEmployees() {
        return departmentMapper.selectAllDepartmentsWithEmployees();
    }
    
    // 業(yè)務(wù)方法:統(tǒng)計(jì)各部門(mén)員工數(shù)量
    public Map<String, Integer> getEmployeeCountByDepartment() {
        List<Department> departments = departmentMapper.selectAllDepartmentsWithEmployees();
        
        return departments.stream()
            .collect(Collectors.toMap(
                Department::getName,
                dept -> dept.getEmployees() != null ? dept.getEmployees().size() : 0
            ));
    }
}

2.4 復(fù)雜場(chǎng)景(多層嵌套)

假設(shè)有一個(gè)多層嵌套的復(fù)雜場(chǎng)景,表關(guān)系如下:

// 公司實(shí)體
public class Company {
    private Long id;
    private String name;
    private List<Department> departments; // 一對(duì)多:公司有多個(gè)部門(mén)
}

// 部門(mén)實(shí)體
public class Department {
    private Long id;
    private String name;
    private Long companyId;           // 所屬公司ID
    private List<Employee> employees; // 一對(duì)多:部門(mén)有多個(gè)員工
    private List<Project> projects;   // 一對(duì)多:部門(mén)有多個(gè)項(xiàng)目
}

// 員工實(shí)體
public class Employee {
    private Long id;
    private String name;
    private String position;      // 新增字段
    private Long deptId;         // 所屬部門(mén)ID
    private List<Skill> skills;  // 多對(duì)多:?jiǎn)T工有多個(gè)技能
}

// 項(xiàng)目實(shí)體
public class Project {
    private Long id;
    private String name;
    private Long deptId;         // 所屬部門(mén)ID
    private Date startDate;      // 新增字段
    private Date endDate;        // 新增字段
}

// 技能實(shí)體
public class Skill {
    private Long id;
    private String name;
    private String category;     // 新增字段:技能分類(lèi)
}

// 員工技能關(guān)聯(lián)實(shí)體(多對(duì)多中間表)
public class EmployeeSkill {
    private Long id;
    private Long employeeId;
    private Long skillId;
    private Integer proficiency; // 熟練程度
}

對(duì)應(yīng)的Mapper映射如下:

<!-- 更新后的結(jié)果映射,包含所有字段 -->
<resultMap id="CompanyResultMap" type="Company">
    <id property="id" column="company_id"/>
    <result property="name" column="company_name"/>
    <collection property="departments" ofType="Department" resultMap="DepartmentResultMap"/>
</resultMap>

<resultMap id="DepartmentResultMap" type="Department">
    <id property="id" column="dept_id"/>
    <result property="name" column="dept_name"/>
    <result property="companyId" column="company_id"/>
    <collection property="employees" ofType="Employee" resultMap="EmployeeResultMap"/>
    <collection property="projects" ofType="Project" resultMap="ProjectResultMap"/>
</resultMap>

<resultMap id="EmployeeResultMap" type="Employee">
    <id property="id" column="emp_id"/>
    <result property="name" column="emp_name"/>
    <result property="position" column="position"/>
    <result property="deptId" column="dept_id"/>
    <collection property="skills" ofType="Skill" resultMap="SkillResultMap"/>
</resultMap>

<resultMap id="ProjectResultMap" type="Project">
    <id property="id" column="project_id"/>
    <result property="name" column="project_name"/>
    <result property="deptId" column="dept_id"/>
    <result property="startDate" column="start_date"/>
    <result property="endDate" column="end_date"/>
</resultMap>

<resultMap id="SkillResultMap" type="Skill">
    <id property="id" column="skill_id"/>
    <result property="name" column="skill_name"/>
    <result property="category" column="category"/>
</resultMap>

<!-- 更新后的查詢(xún)SQL,包含所有字段 -->
<select id="selectCompanyWithDetails" resultMap="CompanyResultMap">
    SELECT 
        c.id as company_id, 
        c.name as company_name,
        d.id as dept_id, 
        d.name as dept_name,
        d.company_id,
        e.id as emp_id, 
        e.name as emp_name,
        e.position,
        e.dept_id,
        p.id as project_id, 
        p.name as project_name,
        p.dept_id,
        p.start_date,
        p.end_date,
        s.id as skill_id, 
        s.name as skill_name,
        s.category
    FROM company c
    LEFT JOIN department d ON c.id = d.company_id
    LEFT JOIN employee e ON d.id = e.dept_id
    LEFT JOIN project p ON d.id = p.dept_id
    LEFT JOIN employee_skill es ON e.id = es.employee_id
    LEFT JOIN skill s ON es.skill_id = s.id
    WHERE c.id = #{id}
</select>

3. 分次查詢(xún)+Stream處理

還有一種方式是通過(guò)分次(次數(shù)為關(guān)聯(lián)表的個(gè)數(shù))查詢(xún)關(guān)聯(lián)表后,再使用Stream流組裝數(shù)據(jù),下面是通過(guò)分次查詢(xún)+Stream處理查詢(xún)公司詳情信息的方法:

@Service
public class CompanyService {
    
    public Company getCompanyWithDetails(Long companyId) {
        // 1. 查詢(xún)公司
        Company company = companyMapper.selectById(companyId);
        if (company == null) return null;
        
        // 2. 查詢(xún)部門(mén)
        List<Department> departments = departmentMapper.selectByCompanyId(companyId);
        
        // 3. 查詢(xún)員工(批量查詢(xún)避免N+1)
        List<Long> deptIds = departments.stream()
            .map(Department::getId)
            .collect(Collectors.toList());
        List<Employee> employees = employeeMapper.selectByDeptIds(deptIds);
        
        // 4. 使用Stream組裝數(shù)據(jù)
        Map<Long, List<Employee>> employeeMap = employees.stream()
            .collect(Collectors.groupingBy(Employee::getDeptId));
        
        departments.forEach(dept -> 
            dept.setEmployees(employeeMap.getOrDefault(dept.getId(), new ArrayList<>()))
        );
        
        company.setDepartments(departments);
        return company;
    }
}

4. 性能對(duì)比

同樣的業(yè)務(wù)下(查詢(xún)公司詳情信息)他們的性能對(duì)比表如下:

方面JOIN+Collection分次查詢(xún)+Stream
數(shù)據(jù)庫(kù)查詢(xún)次數(shù)1次3次
網(wǎng)絡(luò)開(kāi)銷(xiāo)中等
數(shù)據(jù)庫(kù)壓力單次復(fù)雜查詢(xún)多次簡(jiǎn)單查詢(xún)
內(nèi)存占用可能有重復(fù)數(shù)據(jù)數(shù)據(jù)更緊湊
響應(yīng)時(shí)間穩(wěn)定但可能較長(zhǎng)可能更快(并行查詢(xún))

總結(jié):建議在管理后臺(tái)數(shù)據(jù)展示這樣的小數(shù)據(jù)量場(chǎng)景使用Join+Collection方案,在API接口大數(shù)據(jù)量這樣較大數(shù)據(jù)量使用分次查詢(xún)+Stream方案。

到此這篇關(guān)于MyBatis一對(duì)多關(guān)系映射方式小結(jié)的文章就介紹到這了,更多相關(guān)MyBatis一對(duì)多關(guān)系映射內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 一文掌握J(rèn)ava開(kāi)發(fā)工具M(jìn)aven(簡(jiǎn)單上手)

    一文掌握J(rèn)ava開(kāi)發(fā)工具M(jìn)aven(簡(jiǎn)單上手)

    掌握maven的相關(guān)知識(shí)是Java開(kāi)發(fā)必備的技能,今天通過(guò)本文從入門(mén)安裝開(kāi)始,逐步深入講解maven的相關(guān)知識(shí),包括maven的安裝到簡(jiǎn)單上手maven項(xiàng)目開(kāi)發(fā),感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • Java編程倒計(jì)時(shí)實(shí)現(xiàn)方法示例

    Java編程倒計(jì)時(shí)實(shí)現(xiàn)方法示例

    這篇文章主要介紹了Java編程倒計(jì)時(shí)實(shí)現(xiàn)的三個(gè)示例,三種實(shí)現(xiàn)方法,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-09-09
  • 全面解析Java中的GC與幽靈引用

    全面解析Java中的GC與幽靈引用

    一般的應(yīng)用程序不會(huì)涉及到 Reference 編程, 但是了解這些知識(shí)會(huì)對(duì)理解 GC 的工作原理以及性能調(diào)優(yōu)有一定幫助,在實(shí)現(xiàn)一些基礎(chǔ)性設(shè)施比如緩存時(shí)也可能會(huì)用到,希望本文能有所幫助
    2013-09-09
  • 一篇文章帶你學(xué)習(xí)JAVA MyBatis底層原理

    一篇文章帶你學(xué)習(xí)JAVA MyBatis底層原理

    近來(lái)想寫(xiě)一個(gè)mybatis的分頁(yè)插件,但是在寫(xiě)插件之前肯定要了解一下mybatis具體的工作原理吧,本文就詳細(xì)總結(jié)了MyBatis工作原理,,需要的朋友可以參考下
    2021-09-09
  • Java集合TreeSet用法詳解

    Java集合TreeSet用法詳解

    本文詳細(xì)講解了Java集合TreeSet用法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-12-12
  • java內(nèi)存模型jvm虛擬機(jī)簡(jiǎn)要分析

    java內(nèi)存模型jvm虛擬機(jī)簡(jiǎn)要分析

    Java 內(nèi)存模型的主要目的是定義程序中各種變量的訪(fǎng)問(wèn)規(guī)則, 關(guān)注在虛擬機(jī)中把變量值存儲(chǔ)到內(nèi)存和從內(nèi)存中取出變量值這樣的底層細(xì)節(jié)
    2021-09-09
  • servlet下載文件實(shí)現(xiàn)代碼詳解(五)

    servlet下載文件實(shí)現(xiàn)代碼詳解(五)

    這篇文章主要為大家詳細(xì)介紹了servlet下載文件的實(shí)現(xiàn)代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • java的三種IO模型詳解(BIO、NIO、AIO)

    java的三種IO模型詳解(BIO、NIO、AIO)

    本文介紹了BIO、NIO和AIO三種不同的IO模型,分別分析了它們的工作機(jī)制、實(shí)現(xiàn)方式以及與BIO的對(duì)比,BIO是阻塞的,每個(gè)連接需要一個(gè)線(xiàn)程;NIO是同步非阻塞的,通過(guò)緩沖區(qū)和選擇器實(shí)現(xiàn)I/O多路復(fù)用;AIO是異步的,操作系統(tǒng)處理IO操作,完成后通知應(yīng)用程序
    2024-11-11
  • idea的終端(Terminal)cmd的命令換成linux的命令詳解

    idea的終端(Terminal)cmd的命令換成linux的命令詳解

    本文介紹IDEA配置Git的步驟:安裝Git、修改終端設(shè)置并重啟IDEA,強(qiáng)調(diào)順序,作為個(gè)人經(jīng)驗(yàn)分享,希望提供參考并支持腳本之家
    2025-07-07
  • 淺談Maven resrouce下filtering作用

    淺談Maven resrouce下filtering作用

    Filtering是Maven Resources Plugin的一個(gè)功能,本文主要介紹了淺談Maven resrouce下filtering作用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03

最新評(píng)論

慈溪市| 沂南县| 嘉善县| 潼关县| 嘉义县| 双鸭山市| 北票市| 徐闻县| 黄山市| 绵竹市| 治多县| 乐安县| 平原县| 玉山县| 崇义县| 济宁市| 东乡县| 奉贤区| 灵璧县| 怀远县| 邹城市| 曲阜市| 泰和县| 瑞安市| 密云县| 玉田县| 和田县| 田东县| 岚皋县| 博野县| 酉阳| 杭州市| 灵寿县| 镇康县| 沙河市| 普定县| 缙云县| 元江| 县级市| 盐源县| 子长县|