MyBatis多表的关联查询(一对一、一对多、多对多)

MyBatis是一个基于数据库映射的框架,本文将介绍如何使用MyBatis处理多张数据库表之间的关联关系。

1、一对一查询

一对一外键关联的配置比较简单,以公民实体对象和身份证实体对象为例,在表tab_people(公民表)中添加一个字段“card_id”,作为该表的外键,同时需要保证该字段的唯一性,否则就不是一对一映射关系了,而是一对多映射关系。表tab_people和tab_idcard(身份证表)之间的关联关系如下图所示。

【示例】一对一关联,获取公民信息与身份证号码。

(1)在MySQL数据库创建tab_people表和tab_idcard表,并添加相关数据。

-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tab_people;
DROP TABLE IF EXISTS tab_idcard;

-- 创建“身份证信息”数据表
CREATE TABLE IF NOT EXISTS tab_idcard
( 
	id INT AUTO_INCREMENT PRIMARY KEY COMMENT '身份证ID',
	idCard_code VARCHAR(45) COMMENT '身份证号码'
) COMMENT = '身份证信息表';

-- 创建“公民信息”数据表
CREATE TABLE IF NOT EXISTS tab_people
( 
	id INT AUTO_INCREMENT PRIMARY KEY COMMENT '公民ID',
	NAME VARCHAR(45) NOT NULL COMMENT '公民名称',
	sex VARCHAR(2) COMMENT '公民性别',
	age INT COMMENT '公民年龄',
	card_id INT UNIQUE COMMENT '身份证ID',
	-- 创建外键约束
	FOREIGN KEY fk_card_id (card_id)
	REFERENCES tab_idcard(id)
) COMMENT = '公民信息表';
 
-- 添加数据
INSERT INTO tab_idcard(idCard_code) VALUE('123456789');
INSERT INTO tab_people(NAME,sex,age,card_id) VALUES('pan_junbiao的博客','男',32,1);

(2)创建名称为People.java公民信息的持久化类。

package com.pjb.mybatis.po;

/**
 * 公民信息的持久化类
 * @author pan_junbiao
 **/
public class People
{
    private int id;        //公民ID
    private String name;   //公民名称
    private String sex;    //公民性别
    private int age;       //公民年龄
    private IDcard idcard; //关联的身份证对象

    public int getId()
    {
        return id;
    }

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

    public String getName()
    {
        return name;
    }

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

    public String getSex()
    {
        return sex;
    }

    public void setSex(String sex)
    {
        this.sex = sex;
    }

    public int getAge()
    {
        return age;
    }

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

    public IDcard getIdcard()
    {
        return idcard;
    }

    public void setIdcard(IDcard idcard)
    {
        this.idcard = idcard;
    }
}

(3)创建名称为IDcard.java身份证信息的持久化类。

package com.pjb.mybatis.po;

/**
 * 身份证信息的持久化类
 * @author pan_junbiao
 **/
public class IDcard
{
    private int id;             //身份证ID
    private String idCardCode;  //身份证号码

    public int getId()
    {
        return id;
    }

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

    public String getIdCardCode()
    {
        return idCardCode;
    }

    public void setIdCardCode(String idCardCode)
    {
        this.idCardCode = idCardCode;
    }
}

1.1 方式一:SQL表关联语句

使用SQL表关联语句,获取公民信息,同时关联获取身份证信息。

SQL映射信息如下:

<!-- 公民信息类resultMap -->
<resultMap id="peopleResultMap" type="com.pjb.mybatis.po.People">
    <id property="id" column="people_id" />
    <result property="name" column="name"/>
    <result property="sex" column="sex"/>
    <result property="age" column="age"/>
    <!-- 关联的身份证信息 -->
    <association property="idcard" javaType="com.pjb.mybatis.po.IDcard">
        <id property="id" column="card_id" />
        <result property="idCardCode" column="idCard_code"/>
    </association>
</resultMap>

<!-- 一对一查询:获取公民信息与关联的身份证信息 -->
<select id="getPeopleAndIdcard" parameterType="int" resultMap="peopleResultMap">
    SELECT p.id AS people_id
        ,p.NAME
        ,p.sex
        ,p.age
        ,c.id AS card_id
        ,c.idCard_code
    FROM tab_people p
    LEFT JOIN tab_idcard c ON p.card_id = c.id
    WHERE p.id = #{peopleId}
</select>

编写执行方法:

/**
 * 一对一查询:获取公民信息与关联的身份证信息
 * @author pan_junbiao
 */
@Test
public void getPeopleAndIdcard()
{
    DataConnection dataConnection = new DataConnection();
    SqlSession sqlSession = dataConnection.getSqlSession();
    People people = sqlSession.selectOne("test.getPeopleAndIdcard", 1);
    if (people != null)
    {
        System.out.println("---------------1、公民信息--------------------");
        System.out.println("公民编号:" + people.getId());
        System.out.println("公民名称:" + people.getName());
        System.out.println("公民性别:" + people.getSex());
        System.out.println("公民年龄:" + people.getAge());

        //获取关联的身份证信息信息
        System.out.println("---------------2、身份证信息信息---------------");
        IDcard idCard = people.getIdcard();
        if(idCard!=null)
        {
            System.out.println("身份证ID:" + idCard.getId());
            System.out.println("身份证号码:" + idCard.getIdCardCode());
        }
    }
    sqlSession.close();
}

执行结果:

1.2 方式二:延迟加载

如果不使用表的关联关系,而是通过两个SQL查询语句来分别获取公民信息、身份证信息。

首先开启延迟加载,在MyBatis的全局配置文件SqlMapConfig.xml(mybatis-config.xml)中的<settings>标签中添加“开启延迟加载”配置信息:

<!-- 开启延迟加载开关 -->
<setting name="lazyLoadingEnabled" value="true"/>
 
<!-- 将积极加载改为消极加载(即按需加载),默认值就是false -->
<setting name="aggressiveLazyLoading" value="false"/>

修改获取公民信息的配置信息,如下:

<!-- 公民信息类resultMap -->
<resultMap id="peopleResultMap" type="com.pjb.mybatis.po.People">
    <id property="id" column="id" />
    <result property="name" column="name"/>
    <result property="sex" column="sex"/>
    <result property="age" column="age"/>
    <!-- 引用身份证信息类resultMap -->
    <association property="idcard" select="getIdcard" column="card_id"/>
</resultMap>

<!-- 身份证信息类resultMap -->
<resultMap id="idcardResultMap" type="com.pjb.mybatis.po.IDcard">
    <id property="id" column="id" />
    <result property="idCardCode" column="idCard_code"/>
</resultMap>

<!-- 获取公民信息 -->
<select id="getPeople" parameterType="int" resultMap="peopleResultMap">
    SELECT * FROM tab_people WHERE id = #{id}
</select>

<!-- 获取身份证信息 -->
<select id="getIdcard" parameterType="int" resultMap="idcardResultMap">
    SELECT * FROM tab_idcard WHERE id = #{id}
</select>

执行结果:

2、一对多查询

在日常开发中一对多查询是常见的,也是业务中十分重要的部分。下面将以生产商对象(类Factory)与产品对象(类Product)为例,讲解MyBatis的一对多关联。类Factory与类Product的关联关系如下图。

【示例】建立生产商与产品对象对象的一对多关联,并利用映射关系查询完整的产品信息。

(1)在MySQL数据库创建tab_factory表和tab_product表,并添加相关数据。

-- 创建“生产商信息”数据表
CREATE TABLE IF NOT EXISTS tab_factory
( 
	factory_id INT AUTO_INCREMENT PRIMARY KEY COMMENT '生产商ID',
	NAME VARCHAR(20) NOT NULL COMMENT '生产商名称'
) COMMENT = '生产商信息表';

-- 创建“产品信息”数据表
CREATE TABLE IF NOT EXISTS tab_product
( 
	id INT AUTO_INCREMENT PRIMARY KEY COMMENT '产品ID',
	NAME VARCHAR(20) NOT NULL COMMENT '产品名称',
	price DECIMAL(9,2) NOT NULL COMMENT '产品价格',
	factory_id INT COMMENT '生产商ID'
) COMMENT = '产品信息表';
 
-- 添加数据
INSERT INTO tab_factory(NAME) VALUES('华为公司');
INSERT INTO tab_product(NAME,price,factory_id) VALUES('华为手机',1299,1);
INSERT INTO tab_product(NAME,price,factory_id) VALUES('华为路由器',699,1);

(2)创建名称为Product.java产品信息的持久化类。

package com.pjb.mybatis.po;

import java.math.BigDecimal;

/**
 * 产品信息的持久化类
 * @author pan_junbiao
 **/
public class Product
{
    private int id;           //产品ID
    private String name;      //产品名称
    private BigDecimal price; //产品价格
    private int factoryId;    //生产商ID

    public int getId()
    {
        return id;
    }

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

    public String getName()
    {
        return name;
    }

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

    public BigDecimal getPrice()
    {
        return price;
    }

    public void setPrice(BigDecimal price)
    {
        this.price = price;
    }

    public int getFactoryId()
    {
        return factoryId;
    }

    public void setFactoryId(int factoryId)
    {
        this.factoryId = factoryId;
    }
}

(3)创建名称为Factory.java生产商信息的持久化类。

package com.pjb.mybatis.po;

import java.util.List;

/**
 * 生产商信息的持久化类
 * @author pan_junbiao
 **/
public class Factory
{
    private int factoryId;   //生产商ID
    private String name;     //生产商名称
    private List<Product> productList; //产品列表

    public int getFactoryId()
    {
        return factoryId;
    }

    public void setFactoryId(int factoryId)
    {
        this.factoryId = factoryId;
    }

    public String getName()
    {
        return name;
    }

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

    public List<Product> getProductList()
    {
        return productList;
    }

    public void setProductList(List<Product> productList)
    {
        this.productList = productList;
    }
}

2.1 方式一:SQL表关联语句

使用SQL表关联语句,获取生产商信息,同时关联获取产品列表。

SQL映射信息如下:

<!-- 生产商信息resultMap -->
<resultMap id="factoryResultMap" type="com.pjb.mybatis.po.Factory">
    <id property="factoryId" column="factory_id"/>
    <result property="name" column="factory_name"/>
    <collection property="productList" ofType="com.pjb.mybatis.po.Product">
        <id property="id" column="product_id"/>
        <result property="name" column="product_name"/>
        <result property="price" column="price"/>
        <result property="factoryId" column="factory_id"/>
    </collection>
</resultMap>

<!-- 一对多查询:获取生产商信息与关联的产品列表 -->
<select id="getFactoryAndProductList" parameterType="int" resultMap="factoryResultMap">
    SELECT f.factory_id
        ,f.name AS factory_name
        ,p.id AS product_id
        ,p.name AS product_name
        ,p.price
    FROM tab_factory f
    LEFT JOIN tab_product p ON f.factory_id = p.factory_id
    WHERE f.factory_id = #{factoryId}
</select>

编写执行方法:

/**
 * 一对多查询:获取生产商信息与关联的产品列表
 * @author pan_junbiao
 */
@Test
public void getFactoryAndProductList()
{
    DataConnection dataConnection = new DataConnection();
    SqlSession sqlSession = dataConnection.getSqlSession();
    Factory factory = sqlSession.selectOne("test.getFactoryAndProductList",1);
    if (factory != null)
    {
        System.out.println("---------------1、生产商信息信息--------------");
        System.out.println("生产商编号:" + factory.getFactoryId());
        System.out.println("生产商名称:" + factory.getName());

        //获取关联的身份证信息信息
        System.out.println("---------------2、产品信息信息---------------");
        List<Product> productList = factory.getProductList();
        if(productList!=null && productList.size()>0)
        {
            for(Product product : productList)
            {
                System.out.println("产品编号:" + product.getId());
                System.out.println("产品名称:" + product.getName());
                System.out.println("产品价格:" + product.getPrice());
                System.out.println("生产商编号:" + product.getFactoryId());
                System.out.println("-------------------");
            }
        }

    }
    sqlSession.close();
}

执行结果:

2.2 方式二:延迟加载

修改SQL映射信息如下:

<!-- 生产商信息resultMap -->
<resultMap id="factoryResultMap" type="com.pjb.mybatis.po.Factory">
    <id property="factoryId" column="factory_id"/>
    <result property="name" column="name"/>
    <collection property="productList" column="factory_id" select="getProductList" ofType="com.pjb.mybatis.po.Product"/>
</resultMap>

<!-- 获取生产商信息 -->
<select id="getFactory" parameterType="int" resultMap="factoryResultMap">
    SELECT * FROM tab_factory WHERE factory_id = #{factoryId}
</select>

<!-- 根据生产商ID,获取产品列表 -->
<select id="getProductList" parameterType="int" resultType="com.pjb.mybatis.po.Product">
    SELECT * FROM tab_product WHERE factory_id = #{factoryId}
</select>

执行结果:

3、多对多查询

多对多关联关系是比较特殊的一种关联关系,它与一对一和一对多关联关系不同,需要通过另外的一张表保存多对多的映射关系。下面将以应用系统中的权限分配为例讲解多对多的关联关系,例如用户可以拥有多个系统的操作权限,而一个权限又可以被赋予多个用户,这就是典型的多对多关联映射关系。其中用户表(tab_user)和权限表(tab_user)的表关系如下图所示。

说明:由于多对多关联关系的查询对第3个表进行反复查询,在一定程度上会影响系统的性能效率,所以在应用中尽量少使用多对多关联关系的表结果。

【示例】建立用户对象与权限对象的多对多关联关系,查询用户admin所拥有的权限,以及权限“新闻管理”被赋予了哪些用户。

(1)在MySQL数据库创建用户表(tab_user)、权限表(tab_role)和映射表(tab_mapping),并添加相关数据。

-- 判断数据表是否存在,存在则删除
DROP TABLE IF EXISTS tab_user;
DROP TABLE IF EXISTS tab_role;
DROP TABLE IF EXISTS tab_mapping;
 
-- 创建“用户信息”数据表
CREATE TABLE IF NOT EXISTS tab_user
( 
	id INT AUTO_INCREMENT PRIMARY KEY COMMENT '用户ID',
	NAME VARCHAR(45) NOT NULL COMMENT '用户名称'
) COMMENT = '用户信息表';
 
-- 创建“权限信息”数据表
CREATE TABLE IF NOT EXISTS tab_role
( 
	id INT AUTO_INCREMENT PRIMARY KEY COMMENT '权限ID',
	role_name VARCHAR(45) NOT NULL COMMENT '权限名称'
) COMMENT = '权限信息表';
 
-- 创建“映射信息”数据表
CREATE TABLE IF NOT EXISTS tab_mapping
( 
	id INT AUTO_INCREMENT PRIMARY KEY COMMENT '映射ID',
	user_id INT COMMENT '用户Id',
	role_id INT COMMENT '权限Id'
) COMMENT = '映射信息表';
 
-- 添加数据
INSERT INTO tab_user(NAME) VALUES('admin'),('pan_junbiao的博客');
INSERT INTO tab_role(role_name) VALUES('系统管理员'),('新闻管理员'),('广告管理员');
INSERT INTO tab_mapping(user_id,role_id) VALUES(1,1),(1,2),(1,3),(2,2),(2,3);

(2)创建名称为User.java用户信息的持久化类。

package com.pjb.mybatis.po;

import java.util.List;

/**
 * 用户信息的持久化类
 * @author pan_junbiao
 **/
public class User
{
    private int id;//用户ID
    private String name;//用户名称
    private List<Role> roleList;//引用的权限实体对象集合

    public int getId()
    {
        return id;
    }

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

    public String getName()
    {
        return name;
    }

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

    public List<Role> getRoleList()
    {
        return roleList;
    }

    public void setRoleList(List<Role> roleList)
    {
        this.roleList = roleList;
    }
}

(3)创建名称为Role.java权限信息的持久化类。

package com.pjb.mybatis.po;

/**
 * 权限信息的持久化类
 * @author pan_junbiao
 **/
public class Role
{
    private int id;//权限ID
    private String roleName;//权限名称

    public int getId()
    {
        return id;
    }

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

    public String getRoleName()
    {
        return roleName;
    }

    public void setRoleName(String roleName)
    {
        this.roleName = roleName;
    }
}

(4)编写SQL映射信息。

<!-- 用户信息与权限列表resultMap -->
<resultMap id="userRoleResultMap" type="com.pjb.mybatis.po.User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <collection property="roleList" ofType="com.pjb.mybatis.po.Role">
        <id property="id" column="role_id"/>
        <result property="roleName" column="role_name"/>
    </collection>
</resultMap>

<!-- 多对多查询:获取用户信息与关联的权限列表 -->
<select id="getUserAndRole" resultMap="userRoleResultMap">
    SELECT u.id AS user_id
        ,u.name AS user_name
        ,r.id AS role_id
        ,r.role_name
    FROM tab_user u, tab_role r, tab_mapping m
    WHERE u.id = m.user_id
    AND r.id = m.role_id
</select>

(5)编写执行方法。

/**
 * 多对多查询:获取用户信息与关联的权限列表
 * @author pan_junbiao
 */
@Test
public void getUserAndRole()
{
    DataConnection dataConnection = new DataConnection();
    SqlSession sqlSession = dataConnection.getSqlSession();
    List<User> userList = sqlSession.selectList("test.getUserAndRole");
    if(userList!=null && userList.size()>0)
    {
        //遍历用户列表
        for(User user : userList)
        {
            System.out.println("用户编号:" + user.getId());
            System.out.println("用户名称:" + user.getName());

            //获取权限列表
            List<Role> roleList = user.getRoleList();
            if(roleList!=null && roleList.size()>0)
            {
                System.out.print("用户拥有的权限:");
                for (Role role : roleList)
                {
                    System.out.print(role.getRoleName()+";");
                }
            }
            System.out.println("\n-----------------------------------------------");
        }
    }
    sqlSession.close();
}

执行结果:

发布了354 篇原创文章 · 获赞 208 · 访问量 165万+

猜你喜欢

转载自blog.csdn.net/pan_junbiao/article/details/103521988