MyBatis使用篇(六)—— MyBatis关联查询

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_36378917/article/details/85111579

1、建立测试数据模型

1.1 业务模型分析

  该测试案例模拟一个银行批量购买理财产品的业务。用户在网银理财购买页面勾选多款理财产品(如10款),然后网银系统生成一个批次号,该批次号对应这一批(如10款)的理财产品数据,一批中的每一款理财产品会有一个产品号,对应该理财产品的信息。

  批量转账的业务逻辑涉及4张表,分别是用户表、批次表、批次明细表和理财产品表。
在这里插入图片描述
  这里的customer表对应购买的理财产品的用户信息表。batch表存放购买的一批理财产品的批次信息。finacial_products表中存储了每一个理财产品的详细信息。batchdetail表为批次明细表,它是一张中间表,指定对应的一批购买包含了哪些理财产品,对应的理财产品属于哪个购买批次。

  在实体-关系模型中,一个用户可以购买多个批次的理财产品,多个批次的产品也能被同一个用户购买;一个产品批次中包含多个理财产品,而一个理财产品也能被多个产品批次包含。
在这里插入图片描述

1.2 根据业务创建测试表

  创建finacial_products表(理财产品表),并插入如下测试数据,具体SQL语句代码如下:

DROP TABLE IF EXISTS `finacial_products`;

CREATE TABLE `finacial_products` (
  `product_id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL COMMENT '理财产品名称',
  `price` float(10,1) NOT NULL COMMENT '理财产品定价',
  `detail` text COMMENT '理财产品描述',
  `pic` varchar(64) DEFAULT NULL COMMENT '理财产品图片',
  `invasttime` datetime NOT NULL COMMENT '理财产品收益日期',
  PRIMARY KEY (`product_id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

/*Data for the table `finacial_products` */

insert  into `finacial_products`(`product_id`,`name`,`price`,`detail`,`pic`,`invasttime`) values (1,'一起富',5000.0,'投资少,风险小','img001','2017-06-21 00:00:00'),(2,'惠薪富',10000.0,'收益稳健','img002','2017-05-03 00:00:00'),(3,'安富尊容',15000.0,'年收益率提升5%','img003','2017-07-18 00:00:00'),(4,'富津利',2000.0,'企划收益率','img004','2017-04-11 00:00:00');

  创建后的数据表如下所示:
在这里插入图片描述
  创建customer表(用户表),并插入如下测试数据,具体SQL语句代码如下:


DROP TABLE IF EXISTS `customer`;

CREATE TABLE `customer` (
  `cus_id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `acno` varchar(32) DEFAULT NULL COMMENT '卡号',
  `gender` varchar(4) DEFAULT NULL COMMENT '性别',
  `phone` varchar(256) DEFAULT NULL COMMENT '电话',
  PRIMARY KEY (`cus_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

/*Data for the table `customer` */

insert  into `customer`(`cus_id`,`username`,`acno`,`gender`,`phone`) values (1,'刘云','6228286666666','男','13800000000'),(2,'李健','622848111111','男','13811111111'),(3,'张丽丽','622848333333','女','13822222222');

  创建后的数据表如下所示:
在这里插入图片描述
  创建batch表(批次表),并插入如下测试数据,具体SQL语句代码如下:

DROP TABLE IF EXISTS `batch`;

CREATE TABLE `batch` (
  `batch_id` int(11) NOT NULL AUTO_INCREMENT,
  `cus_id` int(11) NOT NULL COMMENT '创建批次用户id',
  `number` varchar(32) NOT NULL COMMENT '批次编码',
  `createtime` datetime NOT NULL COMMENT '创建批次时间',
  `note` varchar(100) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`batch_id`),
  KEY `FK_batch_1` (`cus_id`),
  CONSTRAINT `FK_batch_id` FOREIGN KEY (`cus_id`) REFERENCES `customer` (`cus_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `batch` */

insert  into `batch`(`batch_id`,`cus_id`,`number`,`createtime`,`note`) values (1,1,'00001','2017-07-22 00:00:00','首次购买'),(2,3,'00002','2017-03-11 00:00:00','委托购买');

  创建后的数据表如下所示:
在这里插入图片描述
  创建batchdetail表(批次明细表),并插入如下测试数据,具体SQL语句代码如下:

DROP TABLE IF EXISTS `batchdetail`;

CREATE TABLE `batchdetail` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `batch_id` int(11) NOT NULL COMMENT '批次id',
  `product_id` int(11) NOT NULL COMMENT '理财产品id',
  `product_num` int(11) DEFAULT NULL COMMENT '理财产品购买数量',
  PRIMARY KEY (`id`),
  KEY `FK_batchdetail_1` (`batch_id`),
  KEY `FK_batchdetail_2` (`product_id`),
  CONSTRAINT `FK_batchdetai_1` FOREIGN KEY (`batch_id`) REFERENCES `batch` (`batch_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `FK_batchdetai_2` FOREIGN KEY (`product_id`) REFERENCES `finacial_products` (`product_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

/*Data for the table `batchdetail` */

insert  into `batchdetail`(`id`,`batch_id`,`product_id`,`product_num`) values (1,1,1,2),(2,1,2,1),(3,1,3,1),(4,2,1,2),(5,2,2,1);

  创建后的数据表如下所示:
在这里插入图片描述

1.3 创建实体类

  在com.ccff.mybatis.model包下创建名为“FinacialProduct”的实体类,具体代码如下:

package com.ccff.mybatis.model;

import java.util.Date;
import java.util.Set;

public class FinacialProduct {
    private int product_id;
    private String name;
    private float price;
    private String detail;
    private String pic;
    private Date invasttime;
    private Set<Batch> batches;

    public int getProduct_id() {
        return product_id;
    }

    public void setProduct_id(int product_id) {
        this.product_id = product_id;
    }

    public String getName() {
        return name;
    }

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

    public float getPrice() {
        return price;
    }

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

    public String getDetail() {
        return detail;
    }

    public void setDetail(String detail) {
        this.detail = detail;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public Date getInvasttime() {
        return invasttime;
    }

    public void setInvasttime(Date invasttime) {
        this.invasttime = invasttime;
    }

    public Set<Batch> getBatches() {
        return batches;
    }

    public void setBatches(Set<Batch> batches) {
        this.batches = batches;
    }

    @Override
    public String toString() {
        return "FinacialProduct{" +
                "product_id=" + product_id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", detail='" + detail + '\'' +
                ", pic='" + pic + '\'' +
                ", invasttime=" + invasttime +
                ", batches=" + batches +
                '}';
    }
}

  在com.ccff.mybatis.model包下创建名为“Customer”的实体类,具体代码如下:

package com.ccff.mybatis.model;

import java.util.Set;

public class Customer {
    private int cus_id;
    private String username;
    private String acno;
    private String gender;
    private String phone;
    private Set<Batch> batches;

    public int getCus_id() {
        return cus_id;
    }

    public void setCus_id(int cus_id) {
        this.cus_id = cus_id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAcno() {
        return acno;
    }

    public void setAcno(String acno) {
        this.acno = acno;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public Set<Batch> getBatches() {
        return batches;
    }

    public void setBatches(Set<Batch> batches) {
        this.batches = batches;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "cus_id=" + cus_id +
                ", username='" + username + '\'' +
                ", acno='" + acno + '\'' +
                ", gender='" + gender + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

  在com.ccff.mybatis.model包下创建名为“Batch”的实体类,具体代码如下:

package com.ccff.mybatis.model;

import java.util.Date;
import java.util.Set;

public class Batch {
    private int batch_id;
    private String number;
    private Date createtime;
    private String note;
    private Customer customer;
    private Set<FinacialProduct> finacialProducts;

    public int getBatch_id() {
        return batch_id;
    }

    public void setBatch_id(int batch_id) {
        this.batch_id = batch_id;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    public Date getCreatetime() {
        return createtime;
    }

    public void setCreatetime(Date createtime) {
        this.createtime = createtime;
    }

    public String getNote() {
        return note;
    }

    public void setNote(String note) {
        this.note = note;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public Set<FinacialProduct> getFinacialProducts() {
        return finacialProducts;
    }

    public void setFinacialProducts(Set<FinacialProduct> finacialProducts) {
        this.finacialProducts = finacialProducts;
    }

    @Override
    public String toString() {
        return "Batch{" +
                "batch_id=" + batch_id +
                ", number='" + number + '\'' +
                ", createtime=" + createtime +
                ", note='" + note + '\'' +
                ", customer=" + customer +
                '}';
    }
}

  这里需要注意的是,在定义实体时,若定义的是双向关联,即双方的属性中均有对方对象作为域属性出现,那么它们在定义各自的toString方法时需要注意,只让某一方可以输出另一方即可,不要让双方的toString方法均可输出对方。这样会形成递归调用,程序出错。

1.3 创建接口Dao

  在com.ccff.mybatis.dao包下创建名为“IFinacialDao”的接口类,用于之后的测试接口方法的编写。具体代码如下:

package com.ccff.mybatis.dao;

public interface IFinacialDao {
}

1.4 创建SQL映射文件

  在config/sqlmap文件夹下创建名为“FinacialMapper”的SQL映射文件,具体代码如下:

<?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.ccff.mybatis.dao.IFinacialDao">
</mapper>

  并将创建好的SQL映射文件添加到MyBatis的全局配置文件中,具体代码如下:

<mappers>
    <mapper resource="sqlmap/UserMapper.xml"/>
    <mapper resource="sqlmap/StudentMapper.xml"/>
    <mapper resource="sqlmap/BasketballPlayerMapper.xml"/>
    <mapper resource="sqlmap/FinacialMapper.xml"/>
</mappers>

1.5 创建测试类

  在com.ccff.mybatis.test包下创建名为“FinacialTest”的测试类,具体代码如下:

package com.ccff.mybatis.test;

import com.ccff.mybatis.dao.IFinacialDao;
import com.ccff.mybatis.datasource.DataConnection;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;

import java.io.IOException;

public class FinacialTest {
    private SqlSession sqlSession;
    private IFinacialDao finacialDao;

    @Before
    public void init() {
        DataConnection dataConnection = new DataConnection();
        try {
            sqlSession = dataConnection.getSqlSession();
            finacialDao = sqlSession.getMapper(IFinacialDao.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @After
    public void tearDown(){
        if (sqlSession != null)
            sqlSession.close();
    }
}

2、一对多查询

  一对多关联查询是指,在查询一方对象的时候,同时将其所关联的多方对象也都查询出来。

  一对多查询测试案例的需求是:根据用户id查询该用户信息及用户所购买的理财产品批次信息。

2.1 修改实体类

  修改Customer实体类,重写该类的toString方法,具体代码如下:

@Override
    public String toString() {
        return "Customer{" +
                "cus_id=" + cus_id +
                ", username='" + username + '\'' +
                ", acno='" + acno + '\'' +
                ", gender='" + gender + '\'' +
                ", phone='" + phone + '\'' +
                ", batches=" + batches +
                '}';
    }

2.2 修改Dao接口

  修改IFinacialDao接口,添加selectCustomerByIdUnion方法和selectCustomerByIdAlone方法,具体代码如下:

package com.ccff.mybatis.dao;

import com.ccff.mybatis.model.Customer;

public interface IFinacialDao {
    //一对多查询:根据用户id查询该用户信息及用户所购买的理财产品批次信息。
    Customer selectCustomerByIdUnion(int cus_id);
    Customer selectCustomerByIdAlone(int cus_id);
}

2.3 修改SQL映射文件

2.3.1 通过多表连接查询方式实现

  首先采用多表连接查询的方式实现, 修改SQL映射文件,添加相应的SQL语句,具体配置如下:

<?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.ccff.mybatis.dao.IFinacialDao">
    <resultMap id="CustomerMapper" type="Customer">
        <id column="cus_id" property="cus_id" />
        <result column="username" property="username" />
        <result column="acno" property="acno" />
        <collection property="batches" ofType="Batch">
            <id column="batch_id" property="batch_id" />
            <result column="number" property="number" />
            <result column="createtime" property="createtime" />
            <result column="note" property="note" />
        </collection>
    </resultMap>
    <select id="selectCustomerByIdUnion" resultMap="CustomerMapper">
        select customer.cus_id,customer.username,customer.acno,batch.batch_id,batch.number,batch.createtime,batch.note
        from customer,batch
        where customer.cus_id = batch.cus_id and customer.cus_id = #{cus_id}
    </select>
</mapper>

  在上述的SQL配置文件中,使用resultMap可以将数据库字段映射到名称不一样的相应式题类属性,重要的是,可以映射实体类中包裹的其他实体类。这里需要注意的是使用resultMap时,尽管字段名与属性名相同也要写出它们的映射关系,因为MyBatis框架是依据这个resultMap标签封装对象的。

  另外,在映射文件中使用collection标签体现出两个实体对象之间的关联关系。其中属性含义如下:

  • property:指定关联属性,即实体类中的集合属性
  • ofType:实体类中集合属性的泛型类型

2.3.2 通过多表单独查询方式实现

  其次采用多表单独查询的方式实现, 多表连接查询方式是将多张表进行连接,连为一张表后进行查询。其查询的本质是一张表。而多表单独查询方式是多张表各自查询各自的相关内容,需要多张表的联合数据时,则将主表的查询结果联合其它表的查询结果,封装为一个对象。当然,这多个查询是可以跨越多个映射文件的,即是可以跨越多个namespace的。在使用其它namespace的查询时,添加上其所在的namespace即可。

  修改SQL映射文件,添加相应的SQL语句,具体配置如下:

	<!--根据外键cus_id查询batch表-->
    <select id="selectBatchByCustomerId" resultType="Batch">
        select batch_id,number,createtime,note from batch where cus_id = #{cus_id}
    </select>
    <resultMap id="CustomerMapperAlone" type="Customer">
        <id column="cus_id" property="cus_id" />
        <result column="username" property="username" />
        <result column="acno" property="acno" />
        <!-- 关联属性的映射关系 -->
        <!--集合的数据来自于指定的select查询,而该select查询的恭泰参数来自于column指定的字段值-->
        <collection property="batches" ofType="Batch" select="selectBatchByCustomerId" column="cus_id" />
    </resultMap>
    <!--根据cus_id查询customer表-->
    <select id="selectCustomerByIdAlone" resultMap="CustomerMapperAlone">
        select cus_id,username,acno from customer where cus_id = #{cus_id}
    </select>

  其中,映射文件中两个select标签之间的联系为:关联属性collection的数据来自于另一个查询selectBatchByCustomerId。而该查询selectBatchByCustomerId的动态参数“cus_id = #{cus_id}”的值来自于查询selectCustomerByIdAlone的查询结果字段cus_id。如图所示:
在这里插入图片描述

2.4 修改测试类

  在测试类FinacialTest中添加测试方法TestSelectCustomerByIdUnion,具体代码如下:

	@Test
    public void TestSelectCustomerByIdUnion(){
        Customer customer = finacialDao.selectCustomerByIdUnion(1);
        System.out.println(customer);
    }

  执行该测试方法后,即可在控制台看到如下日志信息,说明测试通过。
在这里插入图片描述
  在测试类FinacialTest中添加测试方法TestSelectCustomerByIdAlone,具体代码如下:

	@Test
    public void TestSelectCustomerByIdAlone(){
        Customer customer = finacialDao.selectCustomerByIdAlone(3);
        System.out.println(customer);
    }

  执行该测试方法后,即可在控制台看到如下日志信息,说明测试通过。
在这里插入图片描述

3、多对一查询

  这里的多对一关联查询是指,在查询多方对象的时候,同时将其所关联的一方对象也查询出来。

  由于在查询多方对象时也是一个一个查询,所以多对一关联查询,其实就是一对一关联查询。即一对一关联查询的实现方式与多对一的实现方式是相同的。

  多对一查询测试案例的需求是:查询batch批次表,并在查询时通过外键cus_id查询中customer用户表的数据。

3.1 修改实体类

  将Customer实体类中的toString方法中的batches属性删除,具体代码如下:

	@Override
    public String toString() {
        return "Customer{" +
                "cus_id=" + cus_id +
                ", username='" + username + '\'' +
                ", acno='" + acno + '\'' +
                ", gender='" + gender + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }

  修改Batch实体类,重写其toString方法如下:

	@Override
    public String toString() {
        return "Batch{" +
                "batch_id=" + batch_id +
                ", number='" + number + '\'' +
                ", createtime=" + createtime +
                ", note='" + note + '\'' +
                ", customer=" + customer +
                '}';
    }

3.2 修改Dao接口

  修改IFinacialDao接口,添加selectCustomerById方法,具体代码如下:

package com.ccff.mybatis.dao;

import com.ccff.mybatis.model.Batch;
import com.ccff.mybatis.model.Customer;

import java.util.List;

public interface IFinacialDao {
    //一对多查询:根据用户id查询该用户信息及用户所购买的理财产品批次信息。
    Customer selectCustomerByIdUnion(int cus_id);
    Customer selectCustomerByIdAlone(int cus_id);

    //多对一查询:查询batch批次表,并在查询时通过外键cus_id查询中customer用户表的数据
    List<Batch> selectAllBatchUnion();
    List<Batch> selectAllBatchAlone();
}

3.3 修改SQL配置文件

3.3.1 通过多表连接查询实现

  修改SQL映射文件,添加相应的SQL语句,具体配置如下:

	<!-- 多对一查询——多表连接查询方式 -->
    <resultMap id="BatchMapperUnion" type="Batch">
        <id column="batch_id" property="batch_id" />
        <result column="number" property="number" />
        <result column="createtime" property="createtime" />
        <result column="note" property="note" />
        <association property="customer" javaType="Customer">
            <id column="cus_id" property="cus_id" />
            <result column="username" property="username" />
            <result column="acno" property="acno" />
            <result column="gender" property="gender" />
            <result column="phone" property="phone" />
        </association>
    </resultMap>
    <select id="selectAllBatchUnion" resultMap="BatchMapperUnion">
        select batch.batch_id,batch.number,batch.createtime,batch.note,customer.cus_id,customer.username,customer.acno,customer.gender,customer.phone
        from customer,batch
        where customer.cus_id = batch.cus_id
    </select>

3.3.2 通过多表单独查询实现

  修改SQL映射文件,添加相应的SQL语句,具体配置如下:

	<!-- 多对一查询——多表单独查询方式 -->
    <select id="selectCustomer" resultType="Customer">
        select cus_id,username,acno,gender,phone from customer where cus_id = #{cus_id}
    </select>
    <resultMap id="BatchMapperAlone" type="Batch">
        <id column="batch_id" property="batch_id" />
        <result column="number" property="number" />
        <result column="createtime" property="createtime" />
        <result column="note" property="note" />
        <association property="customer" javaType="Customer" select="selectCustomer" column="cus_id" />
    </resultMap>
    <select id="selectAllBatchAlone" resultMap="BatchMapperAlone">
        select batch_id,number,createtime,note,cus_id from batch
    </select>

3.4 修改测试类

  在测试类FinacialTest中添加测试方法TestSelectAllBatchUnion和TestSelectAllBatchAlone,具体代码如下:

	@Test
    public void TestSelectAllBatchUnion(){
        List<Batch> batches = finacialDao.selectAllBatchUnion();
        for (Batch batch : batches){
            System.out.println(batch);
        }
    }

    @Test
    public void TestSelectAllBatchAlone(){
        List<Batch> batches = finacialDao.selectAllBatchAlone();
        for (Batch batch : batches){
            System.out.println(batch);
        }
    }

  执行TestSelectAllBatchUnion方法后,即可在控制台看到如下日志信息,说明测试通过。
在这里插入图片描述
  执行TestSelectAllBatchAlone方法后,即可在控制台看到如下日志信息,说明测试通过。
在这里插入图片描述

4、多对多查询

  所谓多对多关系,其实是由两个胡凡的一对多关系组成的。一般情况下,多对多关系都会通过一个中间表建立,例如在测试案例业务场景中的batchdetail表。

  通过之前对业务的分析可知,一个批次的理财产品可以包含多个理财产品,而一个理财产品也可能被多个批次包含,所以理财产品的批次和理财产品之间具有多对多的关联关系。

  多对一查询测试案例的需求是:查询finacial_products表,及每个理财产品所被包含的批次信息。

4.1 修改实体类

  在FinacialProduct实体类中重写toString方法,具体代码如下:

	@Override
    public String toString() {
        return "FinacialProduct{" +
                "product_id=" + product_id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", detail='" + detail + '\'' +
                ", pic='" + pic + '\'' +
                ", invasttime=" + invasttime +
                ", batches=" + batches +
                '}';
    }

4.2 修改Dao接口

  修改IFinacialDao接口,添加selectAllFinacialProduct方法,具体代码如下:

package com.ccff.mybatis.dao;

import com.ccff.mybatis.model.Batch;
import com.ccff.mybatis.model.Customer;
import com.ccff.mybatis.model.FinacialProduct;

import java.util.List;

public interface IFinacialDao {
    //一对多查询:根据用户id查询该用户信息及用户所购买的理财产品批次信息。
    Customer selectCustomerByIdUnion(int cus_id);
    Customer selectCustomerByIdAlone(int cus_id);

    //多对一查询:查询batch批次表,并在查询时通过外键cus_id查询中customer用户表的数据
    List<Batch> selectAllBatchUnion();
    List<Batch> selectAllBatchAlone();

    //多对多查询:查询finacial_products表,及每个理财产品所被包含的批次信息
    List<FinacialProduct> selectAllFinacialProduct();
}

4.3 修改SQL配置文件

  修改SQL映射文件,添加相应的SQL语句,具体配置如下:

	<!-- 多对多查询 -->
    <resultMap id="FinacialProductMapper" type="FinacialProduct">
        <id column="product_id" property="product_id" />
        <result column="name" property="name" />
        <result column="price" property="price" />
        <result column="detail" property="detail" />
        <result column="pic" property="pic" />
        <result column="invasttime" property="invasttime" />
        <collection property="batches" ofType="Batch">
            <id column="batch_id" property="batch_id" />
            <result column="number" property="number" />
            <result column="createtime" property="createtime" />
            <result column="note" property="note" />
        </collection>
    </resultMap>
    <select id="selectAllFinacialProduct" resultMap="FinacialProductMapper">
        select finacial_products.*,batch.* from finacial_products,batch,batchdetail
        where finacial_products.product_id = batchdetail.product_id and batch.batch_id = batchdetail.batch_id
    </select>

4.4 修改测试类

  在测试类FinacialTest中添加测试方法TestSelectAllFinacialProduct,具体代码如下:

	@Test
    public void TestSelectAllFinacialProduct(){
        List<FinacialProduct> finacialProducts = finacialDao.selectAllFinacialProduct();
        for (FinacialProduct finacialProduct : finacialProducts){
            System.out.println(finacialProduct);
        }
    }

  执行TestSelectAllBatchAlone方法后,即可在控制台看到如下日志信息,说明测试通过。
在这里插入图片描述

5、自关联查询

5.1 综述

  所谓自关联查询是指,自己即充当一方,又充当多方,是1:n或n:1的变型。例如对于新闻栏目NewsLabel,可以充当一方,即父栏目,也可以充当多方,即子栏目。而反映到数据中的数据表中,只有一张表,这张表具有一个外键,用于表示该栏目的父栏目。一级栏目没有父栏目,所以可以将其外键设置为0,而子栏目则具有外键值。

  为了便于理解,将自关联分为两种情况来讲解。一种是当做1:n讲解,即当前类作为一方,其包含多方的集合域属性。一种是当做n:1讲解,即当前类作为多方,其包含一方的域属性。

5.2 创建数据表

  在mybatis数据库中创建名为“newslabel”的数据表用于测试自关联查询。建表SQL语句以及插入测试数据的SQL语句如下所示:

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `newslabel`
-- ----------------------------
DROP TABLE IF EXISTS `newslabel`;
CREATE TABLE `newslabel` (
  `id` int(5) NOT NULL auto_increment,
  `name` varchar(20) default NULL,
  `pid` int(5) default NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of newslabel
-- ----------------------------
INSERT INTO `newslabel` VALUES ('1', '娱乐新闻', '0');
INSERT INTO `newslabel` VALUES ('2', '体育新闻', '0');
INSERT INTO `newslabel` VALUES ('3', 'NBA', '2');
INSERT INTO `newslabel` VALUES ('4', 'CBA', '2');
INSERT INTO `newslabel` VALUES ('5', '火箭', '3');
INSERT INTO `newslabel` VALUES ('6', '湖人', '3');
INSERT INTO `newslabel` VALUES ('7', '北京金隅', '4');
INSERT INTO `newslabel` VALUES ('8', '浙江广厦', '4');
INSERT INTO `newslabel` VALUES ('9', '青岛双星', '4');
INSERT INTO `newslabel` VALUES ('10', '港台明星', '1');
INSERT INTO `newslabel` VALUES ('11', '内地明星', '1');

5.3 定义实体类

  在com.ccff.mybatis.model包下创建名为“NewsLabel”的实体类,具体代码如下:

package com.ccff.mybatis.model;

import java.util.Set;

public class NewsLabel {
    private int id;
    private String name;
    private Set<NewsLabel> children;

    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 Set<NewsLabel> getChildren() {
        return children;
    }

    public void setChildren(Set<NewsLabel> children) {
        this.children = children;
    }

    @Override
    public String toString() {
        return "INewsLabelDao{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", children=" + children +
                '}';
    }
}

5.4 创建Dao接口

  在com.ccff.mybatis.dao包下创建名为“INewsLabelDao”的接口,具体代码如下:

package com.ccff.mybatis.dao;

import com.ccff.mybatis.model.NewsLabel;

import java.util.List;

public interface INewsLabelDao {

}

5.5 创建SQL映射文件

  在config/sqlmap文件夹下创建名为“NewsLabelMapper”的SQL映射文件,具体代码如下:

<?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.ccff.mybatis.dao.INewsLabelDao">

</mapper>

  创建SQL映射文件完成后,需要将NewsLabelMapper.xml配置文件配置到MyBatis的全局配置文件SqlMapConfig中,具体配置如下:

	<mappers>
        <mapper resource="sqlmap/UserMapper.xml"/>
        <mapper resource="sqlmap/StudentMapper.xml"/>
        <mapper resource="sqlmap/BasketballPlayerMapper.xml"/>
        <mapper resource="sqlmap/FinacialMapper.xml"/>
        <mapper resource="sqlmap/NewsLabelMapper.xml" />
    </mappers>

5.6 创建测试类

  在com.ccff.mybatis.test包下创建名为“NewsLabelTest”的测试类,实现初始化操作及后续操作,具体代码如下:

package com.ccff.mybatis.test;

import com.ccff.mybatis.dao.INewsLabelDao;
import com.ccff.mybatis.datasource.DataConnection;
import com.ccff.mybatis.model.NewsLabel;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.List;

public class NewsLabelTest {
    private SqlSession sqlSession;
    private INewsLabelDao newsLabelDao;

    @Before
    public void init() {
        DataConnection dataConnection = new DataConnection();
        try {
            sqlSession = dataConnection.getSqlSession();
            newsLabelDao = sqlSession.getMapper(INewsLabelDao.class);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @After
    public void tearDown(){
        if (sqlSession != null)
            sqlSession.close();
    }
}

5.7 以一对多方式处理

  以一对多方式处理,即一方可以看到多方。该处理方式的应用场景比较多,例如在页面上点击父栏目,显示出其子栏目。再如,将鼠标定位在窗口中的某菜单项上回显示其所有子菜单项等。

  根据查询需求的不同,有可以分为两种情况:一种是查询出指定栏目的所有子孙栏目,一种是查询出指定栏目及其所有子孙栏目。

5.7.1 查询指定栏目的所有子孙栏目

  根据指定的id,仅查询出其所有子栏目。当然,包括其所有备份的孙子栏目。即,给出的查询id实际为父栏目id。

  在SQL映射文件中具体配置如下:

<!--根据pid查询其子栏目-->
    <resultMap id="newsLabelMapper" type="NewsLabel">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <collection property="children" ofType="NewsLabel" select="selectChildrenByParentId" column="id" />
    </resultMap>
    <select id="selectChildrenByParentId" parameterType="int" resultMap="newsLabelMapper">
        select id,name from newslabel where pid = #{pid}
    </select>

  这里通过select语句的递归调用实现查询所有下级栏目的功能。查询结果的集合数据collection来自于递归调用的selectChildrenByParentId查询。与第一次进行该查询不同的是,第一次的pid动态参数值来自于调用方法传递来的实参,而collection中查询语句的pid动态参数来自于上一次的查询结果的id值。
在这里插入图片描述
  在测试类NewsLabelTest中添加测试方法TestSelectChildrenByParentId,具体代码如下:

	@Test
    public void TestSelectChildrenByParentId(){
        List<NewsLabel> newsLabels = newsLabelDao.selectChildrenByParentId(1);
        for (NewsLabel newsLabel : newsLabels){
            System.out.println(newsLabel);
        }
    }

  运行测试方法TestSelectChildrenByParentId后在控制台得到如下日志输出信息,说明测试通过。
在这里插入图片描述

5.7.2 查询指定栏目及其所有子孙栏目

  这里的查询结果,既要包含指定id的当前栏目,还包括其所有辈分的孙子栏目。即给出的id实际为当前要查询的栏目的id。

  修改SQL映射文件,具体配置如下:

<mapper namespace="com.ccff.mybatis.dao.INewsLabelDao">
    <!--以一对多方式处理:查询指定栏目的所有子孙栏目-->
    <resultMap id="newsLabelMapper" type="NewsLabel">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <collection property="children" ofType="NewsLabel" select="selectChildrenByParentId" column="id" />
    </resultMap>
    <select id="selectChildrenByParentId" parameterType="int" resultMap="newsLabelMapper">
        select id,name from newslabel where pid = #{pid}
    </select>

    <!--以一对多方式处理:查询指定栏目及其所有子孙栏目-->
    <resultMap id="newsLabelMapper1" type="NewsLabel">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <collection property="children" ofType="NewsLabel" select="selectChildrenByParentId" column="id" />
    </resultMap>
    <select id="selectNewsLabelById" parameterType="int" resultMap="newsLabelMapper1">
        select id,name from newslabel where id = #{id}
    </select>
</mapper>

  在测试类NewsLabelTest中添加测试方法TestSelectChildrenByParentId,具体代码如下:

	@Test
    public void TestSelectNewsLabelById(){
        NewsLabel newsLabel = newsLabelDao.selectNewsLabelById(1);
        System.out.println(newsLabel);
    }

  运行测试方法TestSelectNewsLabelById后在控制台得到如下日志输出信息,说明测试通过。
在这里插入图片描述

5.8 以多对一方式处理

  以多对一方式处理,即多方可以看到一方。该处理方式的应用场景,例如在网页上显示当前页面的站内位置。

  测试案例需求:根据pid查询某一父栏目的全部信息。

5.8.1 修改实体类

  修改NewsLabel,使其变成多方可以看到一方的形式,具体代码如下:

package com.ccff.mybatis.model;

public class NewsLabel {
    private int id;
    private String name;
    private NewsLabel parent;

    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 NewsLabel getParent() {
        return parent;
    }

    public void setParent(NewsLabel parent) {
        this.parent = parent;
    }

    @Override
    public String toString() {
        return "NewsLabel{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", parent=" + parent +
                '}';
    }
}

5.8.2 修改SQL映射文件

  修改SQL映射文件,具体配置如下:

	<!--以多对一方式处理:根据pid查询某一父栏目的全部信息-->
    <resultMap id="newsLabelMapper2" type="NewsLabel">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <association property="parent" javaType="NewsLabel" select="selectParentByParentId" column="pid" />
    </resultMap>
    <select id="selectParentByParentId" parameterType="int" resultMap="newsLabelMapper2">
        select id,name,pid from newslabel where id = #{pid}
    </select>

5.8.3 修改测试类

  在测试类NewsLabelTest中添加测试方法TestSelectParentByParentId,具体代码如下:

@Test
    public void TestSelectParentByParentId(){
        NewsLabel newsLabel = newsLabelDao.selectParentByParentId(3);
        System.out.println(newsLabel);
    }

  运行测试方法TestSelectParentByParentId后在控制台得到如下日志输出信息,说明测试通过。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_36378917/article/details/85111579