zl程序教程

您现在的位置是:首页 >  其他

当前栏目

Mybatis 实战:一对多关系

2023-04-18 14:06:18 时间

文章目录

一 背景

1.1 举例

公司有若干名员工,此时,该公司与其员工之间的关系就属于一对多的关系。根据以上信息,我们可以创建公司信息与员工信息两张表。其中,公司表沿用上一个例子中的表。根据公司编号(ID)查询公司信息及其员工信息。

1.2 相关概念

  • constructor - 用于在实例化类时,注入结果到构造方法中。
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能;
    • arg - 将被注入到构造方法的一个普通结果。
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能。
  • result – 注入到字段或 JavaBean 属性的普通结果。
  • association – 一个复杂类型的关联;许多结果将包装成这种类型。
    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用。
  • collection – 一个复杂类型的集合。
    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用。
  • discriminator – 使用结果值来决定使用哪个 resultMap。
    • case – 基于某些值的结果映射。
      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

** ResultMap 的属性列表**

属性

描述

id

当前命名空间中的一个唯一标识,用于标识一个结果映射。

type

类的完全限定名, 或者一个类型别名(关于内置的类型别名)。

autoMapping

如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

二 操作步骤

由于上一个例子中已经创建数据库,因此不再创建,本例中仅需创建员工信息表和公司表。

2.1 创建表

-- company: table
CREATE TABLE `company`
(
    `id`   int(11)     NOT NULL AUTO_INCREMENT COMMENT '公司编号',
    `name` varchar(20) NOT NULL COMMENT '公司名称',
    PRIMARY KEY (`id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 4
  DEFAULT CHARSET = utf8 COMMENT ='公司信息表';

-- staff: table
CREATE TABLE `staff`
(
    `id`     int(11)     NOT NULL AUTO_INCREMENT COMMENT '员工编号',
    `name`   varchar(20) NOT NULL COMMENT '员工姓名',
    `age`    int(11)     NOT NULL COMMENT '员工年龄',
    `id_com` int(11)     NOT NULL COMMENT '公司编号',
    PRIMARY KEY (`id`),
    KEY `staff_company_id_fk` (`id_com`),
    CONSTRAINT `staff_company_id_fk` FOREIGN KEY (`id_com`) REFERENCES `company` (`id`)
) ENGINE = InnoDB
  AUTO_INCREMENT = 6
  DEFAULT CHARSET = utf8 COMMENT ='员工表';

-- No native definition for element: staff_company_id_fk (index)

INSERT INTO mybatis.company (id, name) VALUES (1, 'A_LTD');
INSERT INTO mybatis.company (id, name) VALUES (2, 'B_LTD');
INSERT INTO mybatis.company (id, name) VALUES (3, 'C_LTD');

INSERT INTO mybatis.staff (id, name, age, id_com) VALUES (1, 'Poll', 32, 1);
INSERT INTO mybatis.staff (id, name, age, id_com) VALUES (2, 'Shelly', 30, 2);
INSERT INTO mybatis.staff (id, name, age, id_com) VALUES (3, 'Musk', 20, 1);
INSERT INTO mybatis.staff (id, name, age, id_com) VALUES (4, 'John', 50, 2);
INSERT INTO mybatis.staff (id, name, age, id_com) VALUES (5, 'Maria', 38, 2);

2.2 创建项目

mvn archetype:generate -DgroupId=com.ivandu.mybatis -D artifactId=mybatis -DarchetypeArtifactId=maven-archetype-quickstart

2.3 项目配置

pom.xml 、mybatis.cfg.xml、log4j.properties 与上一篇文章《Mybatis 实战: 一对一关系》中一致,可共参考。

2.4 创建 model

本例中的 model 包括员工和公司类:

package com.ivandu.mybatis.model;

public class Staff {
    private Integer id;
    private String name;
    private Integer age;

    public Staff() {
    }

    public Staff(Integer id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Staff{" +
                "id=" + id +
                ", name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}
package com.ivandu.mybatis.model;

import java.util.List;

public class Company {
    private Integer id;
    private String name;
    private List<Staff> staffs;

    public Company(){}

    public Company(Integer id, String name, List<Staff> staffs) {
        this.id = id;
        this.name = name;
        this.staffs = staffs;
    }

    public Integer getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public List<Staff> getStaffs() {
        return staffs;
    }

    public void setStaffs(List<Staff> staffs) {
        this.staffs = staffs;
    }

    @Override
    public String toString() {
        return "Company{" +
                "cid=" + id +
                ", name='" + name + ''' +
                ", staffs=" + staffs +
                '}';
    }
}

2.5 创建 mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org/DTD Mapper 3.0" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ivandu.mybatis.mapper.CompanyMapper">
    <select id="selectCompanyAndStaffById" parameterType="Integer" resultMap="companyMap">
        select com.id, com.name, staff.id sid, staff.name sname, staff.age
        from company com left outer join staff on com.id = staff.id_com
        where com.id = #{cid}
    </select>

    <resultMap id="companyMap" type="Company">
        <id property="id" column="id" />
        <result property="name" column="name" />
        <collection property="staffs" ofType="Staff" resultMap="staffsMap" column="staffs"/>
    </resultMap>

    <resultMap id="staffsMap" type="Staff" autoMapping="true">
        <id property="id" column="sid"/>
        <result property="name" column="sname" />
        <result property="age" column="age" />
    </resultMap>
</mapper>
package com.ivandu.mybatis.mapper;

import com.ivandu.mybatis.model.Company;

public interface CompanyMapper {
    Company selectCompanyAndStaffById(Integer cid) throws Exception;
}

2.6 测试

package com.ivandu.mybatis;

import com.ivandu.mybatis.mapper.CompanyMapper;
import com.ivandu.mybatis.model.Company;
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.junit.BeforeClass;
import org.junit.Test;

import java.io.Reader;

public class OneToMany {
    public static SqlSessionFactory sqlSessionFactory;

    @BeforeClass
    public static void init() throws Exception {
        String resource = "mybatis.cfg.xml";
        Reader reader = Resources.getResourceAsReader(resource);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        reader.close();
    }

    @Test
    public void selectCompanyAndStaffById() {
        Company company;
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try (sqlSession) {
            CompanyMapper companyMapper = sqlSession.getMapper(CompanyMapper.class);
            company = companyMapper.selectCompanyAndStaffById(2);
            System.out.println(company);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三 故障排除及总结

3.1 运行报错

  • Expected one result (or null) to be returned by selectOne(), but found.

该报错是由于在 Company 和 Staff 类中都有 id 和 name 两个相同属性,给相同属性取别名并更新 id 和 result 的 colunm 值,使用别名后正常。

  • Git 提交信息错误,修改错误提交信息,重新提交代码。
git log
git rebase -i HEAD~1 # 找到对应的 commit id,将前面的 pick 改为 edit
git rebase --continue # 此时即可修改提交记录
git commit --amend -s
git push -u -f

四 参考资料及源码