Mybatis巧用@Many注解让一条SQL联合查询语句完成一对多查询

MyBatis中常用的一对多查询是 N+1 方式的查询,即先查询主表再查询子表.这就是著名的 [n+1 问题] 。如果记录很多,这种查询效率是非常低效的。

有没有只需要一条SQL查询就能搞定一对多关系的呢?通过反复阅读 @Many 注解源码,发现里面有resultMap字段,这就跟XML方式的ResultMap对应上了.

ResultMap描述了如何从数据库结果集中加载对象,是最复杂也是最强大的元素

/**
 * The annotation that specify the nested statement for retrieving collections.
 *
 * @see Result
 * @see Results
 * @author Clinton Begin
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Many {
  /**
   * Returns the columnPrefix.
   *
   * @return the columnPrefix.
   * @since 3.5.5
   */
  String columnPrefix() default "";

  /**
   * Returns the result map id used to map collection.
   *
   * @return the result map id
   * @since 3.5.5
   */
  String resultMap() default "";

  /**
   * Returns the statement id that retrieves collection.
   *
   * @return the statement id
   */
  String select() default "";

  /**
   * Returns the fetch strategy for nested statement.
   *
   * @return the fetch strategy
   */
  FetchType fetchType() default FetchType.DEFAULT;

}

再有 @Results 注解的 id 属性对应上了XML里的ResultMap 的 id.这样一来通过注解就能实现一条SQL语句完成一对多的查询了.

废话少说,直接上源码!

数据表结构

1.主表(bfs_nota1001)

CREATE TABLE `bfs_nota1001` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '与业务无关的自增主键',
  `f_notaunikey` char(32) NOT NULL COMMENT '标识一条记录的key,由业务字段的MD5成的32个字节的字符串',
  `f_head` char(12) NOT NULL COMMENT '报文头,长度(字节): 12',
  `f_msgtype` varchar(100) NOT NULL COMMENT '消息类型,',
  `f_drivercode` char(9) NOT NULL COMMENT '设备编号,长度(字节): 9',
  `f_qr_cdrivercode` varchar(9) NOT NULL COMMENT 'QR设备编号,长度(字节): 9',
  `f_qr_fdatetime` timestamp NOT NULL COMMENT 'QR封包时间',
  `f_qr_randomnum` char(5) NOT NULL COMMENT 'QR钞捆序号,长度(字节): 5',
  PRIMARY KEY (`id`),
  UNIQUE KEY `bfs_nota1001_un` (`f_notaunikey`)
);

INSERT INTO `bfs_nota1001` VALUES (1,'3c496a9cac5986b5e9dc876f4f0401ff','GDAP20200527','1001','M02000930','M02000429','2020-05-27 09:28:39','20022');

2.子表(bfs_nota1001_detail)

CREATE TABLE `bfs_nota1001_detail` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '与业务无关的自增主键',
  `f_notaunikey` char(32) NOT NULL COMMENT '标识一条记录的key,由相关的表bfs_nota1001的字段f_notaunikey来赋值',
  `f_detailunikey` char(32) NOT NULL COMMENT '标识一条记录的key,由业务字段的MD5成的32个字节的字符串',
  `f_moneyslogan` char(2) NOT NULL COMMENT '钞口号,长度(字节): 2',
  `f_keynum` char(5) NOT NULL COMMENT '把号,长度(字节): 5',
  `f_backcode` char(5) NOT NULL COMMENT '银行缩写,长度(字节): 5',
  `f_detailbacksx` char(9) NOT NULL COMMENT '银行代码,长度(字节): 9',
  `f_drvercode` char(9) NOT NULL COMMENT '设备编号,长度(字节): 9',
  `f_btime` timestamp NOT NULL COMMENT '时间,长度(字节): 12',
  `f_coupons` char(3) NOT NULL COMMENT '券别,长度(字节): 3',
  `f_couponcount` int(11) NOT NULL COMMENT '张数,长度(字节): 4',
  `f_couponvalue` int(11) NOT NULL COMMENT '金额,长度(字节): 6',
  `f_revision` varchar(10) NOT NULL COMMENT '版本,长度(字节): 10',
  PRIMARY KEY (`id`),
  UNIQUE KEY `bfs_nota1001_detail_un` (`f_detailunikey`),
  KEY `bfs_nota1001_detail_key` (`f_notaunikey`)
);

INSERT INTO `bfs_nota1001_detail` VALUES (1,'3c496a9cac5986b5e9dc876f4f0401ff','e5d73440e633b7783eb3fb957ae60e3','02','00019','51000','771600110','M02000429','2020-05-27 09:27:49','100',100,10000,'2015FIT   '),(2,'3c496a9cac5986b5e9dc876f4f0401ff','ad2a34af3038a87f263b8f9ea771e4f1','02','00016','51000','771600110','M02000429','2020-05-27 09:27:51','100',100,10000,'2015FIT   '),(3,'3c496a9cac5986b5e9dc876f4f0401ff','75a42347de99ece9d706dbb7c804ca0d','02','00018','51000','771600110','M02000429','2020-05-27 09:27:54','100',100,10000,'2015FIT   '),(4,'3c496a9cac5986b5e9dc876f4f0401ff','92e78b01edb6e03f3746c284e80ebc73','02','00014','51000','771600110','M02000429','2020-05-27 09:27:56','100',100,10000,'2015FIT   '),(5,'3c496a9cac5986b5e9dc876f4f0401ff','b95e2e493947c5f522546b6fa4d3ba3d','01','00022','51000','771600110','M02000429','2020-05-27 09:27:59','100',100,10000,'2015FIT   '),(6,'3c496a9cac5986b5e9dc876f4f0401ff','a5efae152a9da726807e5c9a743c1b6d','01','00025','51000','771600110','M02000429','2020-05-27 09:28:01','100',100,10000,'2015FIT   '),(7,'3c496a9cac5986b5e9dc876f4f0401ff','68cb897a34180f92a606aa5a73f20a0b','02','00015','51000','771600110','M02000429','2020-05-27 09:28:04','100',100,10000,'2015FIT   '),(8,'3c496a9cac5986b5e9dc876f4f0401ff','24ffd138d550e59243b1c65504c24d6a','01','00017','51000','771600110','M02000429','2020-05-27 09:28:06','100',100,10000,'2015FIT   '),(9,'3c496a9cac5986b5e9dc876f4f0401ff','ff9b18003d7f730a77deabeba6fd453d','02','00012','51000','771600110','M02000429','2020-05-27 09:28:09','100',100,10000,'2015FIT   '),(10,'3c496a9cac5986b5e9dc876f4f0401ff','c9ab32450ec9154b84b3ad98918030e2','01','00024','51000','771600110','M02000429','2020-05-27 09:28:12','100',100,10000,'2015FIT   ');

实体类:

1.主类(BfsNota1001)

package wjw.test.mybatisplus.slave.vo;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.List;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;

public class BfsNota1001 implements Serializable {

  private static final long serialVersionUID = 1L;

  @TableId(
      type = IdType.AUTO)
  /**
   * 与业务无关的自增主键
   */
  private Long id;

  /**
   * 标识一条记录的key,由业务字段的md5成的32个字节的字符串
   */
  private String fNotaunikey;

  /**
   * 报文头,长度(字节)
   */
  private String fHead;

  /**
   * 消息类型,
   */
  private String fMsgtype;

  /**
   * 设备编号,长度(字节)
   */
  private String fDrivercode;

  /**
   * qr设备编号,长度(字节)
   */
  private String fQrCdrivercode;

  /**
   * qr封包时间
   */
  private LocalDateTime fQrFdatetime;

  /**
   * qr钞捆序号,长度(字节)
   */
  private String fQrRandomnum;

  private List<BfsNota1001Detail> details;

  public BfsNota1001() {
  }

  public Long getId() {
    return id;
  }

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

  public String getfNotaunikey() {
    return fNotaunikey;
  }

  public void setfNotaunikey(String fNotaunikey) {
    this.fNotaunikey = fNotaunikey;
  }

  public String getfHead() {
    return fHead;
  }

  public void setfHead(String fHead) {
    this.fHead = fHead;
  }

  public String getfMsgtype() {
    return fMsgtype;
  }

  public void setfMsgtype(String fMsgtype) {
    this.fMsgtype = fMsgtype;
  }

  public String getfDrivercode() {
    return fDrivercode;
  }

  public void setfDrivercode(String fDrivercode) {
    this.fDrivercode = fDrivercode;
  }

  public String getfQrCdrivercode() {
    return fQrCdrivercode;
  }

  public void setfQrCdrivercode(String fQrCdrivercode) {
    this.fQrCdrivercode = fQrCdrivercode;
  }

  public LocalDateTime getfQrFdatetime() {
    return fQrFdatetime;
  }

  public void setfQrFdatetime(LocalDateTime fQrFdatetime) {
    this.fQrFdatetime = fQrFdatetime;
  }

  public String getfQrRandomnum() {
    return fQrRandomnum;
  }

  public void setfQrRandomnum(String fQrRandomnum) {
    this.fQrRandomnum = fQrRandomnum;
  }

  public List<BfsNota1001Detail> getDetails() {
    return details;
  }

  public void setDetails(List<BfsNota1001Detail> details) {
    this.details = details;
  }

  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    builder.append("BfsNota1001 [id=");
    builder.append(id);
    builder.append(", fNotaunikey=");
    builder.append(fNotaunikey);
    builder.append(", fHead=");
    builder.append(fHead);
    builder.append(", fMsgtype=");
    builder.append(fMsgtype);
    builder.append(", fDrivercode=");
    builder.append(fDrivercode);
    builder.append(", fQrCdrivercode=");
    builder.append(fQrCdrivercode);
    builder.append(", fQrFdatetime=");
    builder.append(fQrFdatetime);
    builder.append(", fQrRandomnum=");
    builder.append(fQrRandomnum);
    builder.append(", details=");
    builder.append(details);
    builder.append("]");
    return builder.toString();
  }

}

2.子类(BfsNota1001Detail):

package wjw.test.mybatisplus.slave.vo;

import java.io.Serializable;
import java.time.LocalDateTime;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;

public class BfsNota1001Detail implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(type = IdType.AUTO)
    /**
    * 与业务无关的自增主键
    */
    private Long id;

    /**
    * 标识一条记录的key,由相关的表bfs_nota1001的字段f_notaunikey来赋值
    */
    private String fNotaunikey;

    /**
    * 标识一条记录的key,由业务字段的md5成的32个字节的字符串
    */
    private String fDetailunikey;

    /**
    * 钞口号,长度(字节)
    */
    private String fMoneyslogan;

    /**
    * 把号,长度(字节)
    */
    private String fKeynum;

    /**
    * 银行缩写,长度(字节)
    */
    private String fBackcode;

    /**
    * 银行代码,长度(字节)
    */
    private String fDetailbacksx;

    /**
    * 设备编号,长度(字节)
    */
    private String fDrvercode;

    /**
    * 时间,长度(字节)
    */
    private LocalDateTime fBtime;

    /**
    * 券别,长度(字节)
    */
    private String fCoupons;

    /**
    * 张数,长度(字节)
    */
    private Integer fCouponcount;

    /**
    * 金额,长度(字节)
    */
    private Integer fCouponvalue;

    /**
    * 版本,长度(字节)
    */
    private String fRevision;

    public BfsNota1001Detail() {
    }

    public Long getId() {
      return id;
    }

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

    public String getfNotaunikey() {
      return fNotaunikey;
    }

    public void setfNotaunikey(String fNotaunikey) {
      this.fNotaunikey = fNotaunikey;
    }

    public String getfDetailunikey() {
      return fDetailunikey;
    }

    public void setfDetailunikey(String fDetailunikey) {
      this.fDetailunikey = fDetailunikey;
    }

    public String getfMoneyslogan() {
      return fMoneyslogan;
    }

    public void setfMoneyslogan(String fMoneyslogan) {
      this.fMoneyslogan = fMoneyslogan;
    }

    public String getfKeynum() {
      return fKeynum;
    }

    public void setfKeynum(String fKeynum) {
      this.fKeynum = fKeynum;
    }

    public String getfBackcode() {
      return fBackcode;
    }

    public void setfBackcode(String fBackcode) {
      this.fBackcode = fBackcode;
    }

    public String getfDetailbacksx() {
      return fDetailbacksx;
    }

    public void setfDetailbacksx(String fDetailbacksx) {
      this.fDetailbacksx = fDetailbacksx;
    }

    public String getfDrvercode() {
      return fDrvercode;
    }

    public void setfDrvercode(String fDrvercode) {
      this.fDrvercode = fDrvercode;
    }

    public LocalDateTime getfBtime() {
      return fBtime;
    }

    public void setfBtime(LocalDateTime fBtime) {
      this.fBtime = fBtime;
    }

    public String getfCoupons() {
      return fCoupons;
    }

    public void setfCoupons(String fCoupons) {
      this.fCoupons = fCoupons;
    }

    public Integer getfCouponcount() {
      return fCouponcount;
    }

    public void setfCouponcount(Integer fCouponcount) {
      this.fCouponcount = fCouponcount;
    }

    public Integer getfCouponvalue() {
      return fCouponvalue;
    }

    public void setfCouponvalue(Integer fCouponvalue) {
      this.fCouponvalue = fCouponvalue;
    }

    public String getfRevision() {
      return fRevision;
    }

    public void setfRevision(String fRevision) {
      this.fRevision = fRevision;
    }

    @Override
    public String toString() {
      StringBuilder builder = new StringBuilder();
      builder.append("BfsNota1001Detail [id=");
      builder.append(id);
      builder.append(", fNotaunikey=");
      builder.append(fNotaunikey);
      builder.append(", fDetailunikey=");
      builder.append(fDetailunikey);
      builder.append(", fMoneyslogan=");
      builder.append(fMoneyslogan);
      builder.append(", fKeynum=");
      builder.append(fKeynum);
      builder.append(", fBackcode=");
      builder.append(fBackcode);
      builder.append(", fDetailbacksx=");
      builder.append(fDetailbacksx);
      builder.append(", fDrvercode=");
      builder.append(fDrvercode);
      builder.append(", fBtime=");
      builder.append(fBtime);
      builder.append(", fCoupons=");
      builder.append(fCoupons);
      builder.append(", fCouponcount=");
      builder.append(fCouponcount);
      builder.append(", fCouponvalue=");
      builder.append(fCouponvalue);
      builder.append(", fRevision=");
      builder.append(fRevision);
      builder.append("]");
      return builder.toString();
    }

}

Mapper类:

BfsNota1001Mapper:

package wjw.test.mybatisplus.slave.mapper;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import wjw.test.mybatisplus.slave.vo.BfsNota1001;
import wjw.test.mybatisplus.slave.vo.BfsNota1001Detail;

@Mapper
public interface BfsNota1001Mapper extends BaseMapper<BfsNota1001> {
    @Select("select "+
        "m.*,"+
        "s.id s_id,"+
        "s.f_notaunikey s_f_notaunikey,"+
        "s.f_detailunikey s_f_detailunikey,"+
        "s.f_moneyslogan s_f_moneyslogan,"+
        "s.f_keynum s_f_keynum,"+
        "s.f_backcode s_f_backcode,"+
        "s.f_detailbacksx s_f_detailbacksx,"+
        "s.f_drvercode s_f_drvercode,"+
        "s.f_btime s_f_btime,"+
        "s.f_coupons s_f_coupons,"+
        "s.f_couponcount s_f_couponcount,"+
        "s.f_couponvalue s_f_couponvalue,"+
        "s.f_revision s_f_revision"+
        " from "+
        " bfs_nota1001 m "+
        " left join bfs_nota1001_detail s on "+
        " m.f_notaunikey = s.f_notaunikey "+
        " where "+
        " m.id =#{id} "
    )
    @Results({
      @Result(property = "id", column = "id", id = true),                  // <1>
      @Result(column = "f_notaunikey",property = "fNotaunikey"),
      @Result(column = "f_head",property = "fHead"),
      @Result(column = "f_msgtype",property = "fMsgtype"),
      @Result(column = "f_drivercode",property = "fDrivercode"),
      @Result(column = "f_qr_cdrivercode",property = "fQrCdrivercode"),
      @Result(column = "f_qr_fdatetime",property = "fQrFdatetime"),
      @Result(column = "f_qr_randomnum",property = "fQrRandomnum"),
      @Result(property = "details", many = @Many(resultMap = "1001detailMap", columnPrefix = "s_"))  // <2>
    })
    List<BfsNota1001> get1001(@Param("id")Integer id);
    
    @Select("select * from bfs_nota1001_detail where id=#{id}")
    @Results(id = "1001detailMap", 
      value={@Result(id=true,column="id",property="id"),
      @Result(column = "f_notaunikey",property = "fNotaunikey"),
      @Result(column = "f_detailunikey",property = "fDetailunikey"),
      @Result(column = "f_moneyslogan",property = "fMoneyslogan"),
      @Result(column = "f_keynum",property = "fKeynum"),
      @Result(column = "f_backcode",property = "fBackcode"),
      @Result(column = "f_detailbacksx",property = "fDetailbacksx"),
      @Result(column = "f_drvercode",property = "fDrvercode"),
      @Result(column = "f_btime",property = "fBtime"),
      @Result(column = "f_coupons",property = "fCoupons"),
      @Result(column = "f_couponcount",property = "fCouponcount"),
      @Result(column = "f_couponvalue",property = "fCouponvalue"),
      @Result(column = "f_revision",property = "fRevision")
    })
    List<BfsNota1001Detail> getDetailById(@Param("id")Integer id);    
}

<1>: @Result(column=“id”,property=“Id”, id = true),这个可以不写,也不会报错,但是会导致我们查询结果列表里每个UVO 实体的Id等属性没有值。

<2>: 这个@Result(property = "details", many = @Many(resultMap = "1001detailMap", columnPrefix = "s_"))

property = "details",代表子查询的查询结果关联的实体属性,就是BfsNota1001中的details字段。

resultMap = "1001detailMap" 指向getDetailById方法上的@Results注解里的id值.

事实上,getDetailById这个方法并没有被真的调用,甚至上面 Select 注解中的 SQL 语句也不会被执行。定义这个方法只是因为 @Results 注解必需要依存于一个方法,换句话来说,这个方法只是占位符而已。

columnPrefix, 会为 1001detailMap 中的所有列名都加上一个 s_ 前缀,这样一来就能匹配上联合查询 SQL 语句中实际返回的列名(例如 s_id)了。

测试

package wjw.test.mybatisplus;

import java.util.List;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.ActiveProfiles;

import wjw.test.mybatisplus.slave.mapper.BfsNota1001DetailMapper;
import wjw.test.mybatisplus.slave.mapper.BfsNota1001Mapper;
import wjw.test.mybatisplus.slave.vo.BfsNota1001;

@SpringBootTest(webEnvironment = WebEnvironment.NONE)
public class MybatisPlusTestSlave {
  @Autowired
  private BfsNota1001Mapper b1001Mapper;

  public MybatisPlusTestSlave() {
    //
  }

  @BeforeEach
  public void setup() {
    //
  }

  @AfterEach
  public void stop() {
  }

  @Test
  public void getUnionListObjById() {
    List<BfsNota1001> list = b1001Mapper.get1001(1);
    System.out.println(list);
  }

}

输出结果:

w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [debug,137] - ==>  Preparing: select m.*,s.id s_id,s.f_notaunikey s_f_notaunikey,s.f_detailunikey s_f_detailunikey,s.f_moneyslogan s_f_moneyslogan,s.f_keynum s_f_keynum,s.f_backcode s_f_backcode,s.f_detailbacksx s_f_detailbacksx,s.f_drvercode s_f_drvercode,s.f_btime s_f_btime,s.f_coupons s_f_coupons,s.f_couponcount s_f_couponcount,s.f_couponvalue s_f_couponvalue,s.f_revision s_f_revision from bfs_nota1001 m left join bfs_nota1001_detail s on m.f_notaunikey = s.f_notaunikey where m.id =?
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [debug,137] - ==> Parameters: 1(Integer)
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==    Columns: id, f_notaunikey, f_head, f_msgtype, f_drivercode, f_qr_cdrivercode, f_qr_fdatetime, f_qr_randomnum, s_id, s_f_notaunikey, s_f_detailunikey, s_f_moneyslogan, s_f_keynum, s_f_backcode, s_f_detailbacksx, s_f_drvercode, s_f_btime, s_f_coupons, s_f_couponcount, s_f_couponvalue, s_f_revision
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 1, 3c496a9cac5986b5e9dc876f4f0401ff, e5d73440e633b7783eb3fb957ae60e3, 02, 00019, 51000, 771600110, M02000429, 2020-05-27 17:27:49.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 2, 3c496a9cac5986b5e9dc876f4f0401ff, ad2a34af3038a87f263b8f9ea771e4f1, 02, 00016, 51000, 771600110, M02000429, 2020-05-27 17:27:51.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 3, 3c496a9cac5986b5e9dc876f4f0401ff, 75a42347de99ece9d706dbb7c804ca0d, 02, 00018, 51000, 771600110, M02000429, 2020-05-27 17:27:54.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 4, 3c496a9cac5986b5e9dc876f4f0401ff, 92e78b01edb6e03f3746c284e80ebc73, 02, 00014, 51000, 771600110, M02000429, 2020-05-27 17:27:56.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 5, 3c496a9cac5986b5e9dc876f4f0401ff, b95e2e493947c5f522546b6fa4d3ba3d, 01, 00022, 51000, 771600110, M02000429, 2020-05-27 17:27:59.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 6, 3c496a9cac5986b5e9dc876f4f0401ff, a5efae152a9da726807e5c9a743c1b6d, 01, 00025, 51000, 771600110, M02000429, 2020-05-27 17:28:01.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 7, 3c496a9cac5986b5e9dc876f4f0401ff, 68cb897a34180f92a606aa5a73f20a0b, 02, 00015, 51000, 771600110, M02000429, 2020-05-27 17:28:04.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 8, 3c496a9cac5986b5e9dc876f4f0401ff, 24ffd138d550e59243b1c65504c24d6a, 01, 00017, 51000, 771600110, M02000429, 2020-05-27 17:28:06.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 9, 3c496a9cac5986b5e9dc876f4f0401ff, ff9b18003d7f730a77deabeba6fd453d, 02, 00012, 51000, 771600110, M02000429, 2020-05-27 17:28:09.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [trace,143] - <==        Row: 1, 3c496a9cac5986b5e9dc876f4f0401ff, GDAP20200527, 1001, M02000930, M02000429, 2020-05-27 17:28:39.0, 20022, 10, 3c496a9cac5986b5e9dc876f4f0401ff, c9ab32450ec9154b84b3ad98918030e2, 01, 00024, 51000, 771600110, M02000429, 2020-05-27 17:28:12.0, 100, 100, 10000, 2015FIT   
w.t.m.slave.mapper.BfsNota1001Mapper.get1001 - [debug,137] - <==      Total: 10
[BfsNota1001 [id=1, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fHead=GDAP20200527, fMsgtype=1001, fDrivercode=M02000930, fQrCdrivercode=M02000429, fQrFdatetime=2020-05-27T17:28:39, fQrRandomnum=20022, details=[BfsNota1001Detail [id=1, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=e5d73440e633b7783eb3fb957ae60e3, fMoneyslogan=02, fKeynum=00019, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:27:49, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=2, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=ad2a34af3038a87f263b8f9ea771e4f1, fMoneyslogan=02, fKeynum=00016, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:27:51, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=3, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=75a42347de99ece9d706dbb7c804ca0d, fMoneyslogan=02, fKeynum=00018, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:27:54, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=4, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=92e78b01edb6e03f3746c284e80ebc73, fMoneyslogan=02, fKeynum=00014, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:27:56, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=5, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=b95e2e493947c5f522546b6fa4d3ba3d, fMoneyslogan=01, fKeynum=00022, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:27:59, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=6, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=a5efae152a9da726807e5c9a743c1b6d, fMoneyslogan=01, fKeynum=00025, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:28:01, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=7, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=68cb897a34180f92a606aa5a73f20a0b, fMoneyslogan=02, fKeynum=00015, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:28:04, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=8, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=24ffd138d550e59243b1c65504c24d6a, fMoneyslogan=01, fKeynum=00017, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:28:06, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=9, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=ff9b18003d7f730a77deabeba6fd453d, fMoneyslogan=02, fKeynum=00012, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:28:09, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ], BfsNota1001Detail [id=10, fNotaunikey=3c496a9cac5986b5e9dc876f4f0401ff, fDetailunikey=c9ab32450ec9154b84b3ad98918030e2, fMoneyslogan=01, fKeynum=00024, fBackcode=51000, fDetailbacksx=771600110, fDrvercode=M02000429, fBtime=2020-05-27T17:28:12, fCoupons=100, fCouponcount=100, fCouponvalue=10000, fRevision=2015FIT   ]]]]

经过测试,上面的 Mapper 完美地完成了任务。值得一提的是,引用的 ResultMap 不一定要定义在同一个 Mapper 中,也可以用全限定名去引用 Mapper 外部的 ResultMap。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
MyBatis Plus提供了多种方式来实现多表一对多查询,以下是其中一种常用的方法: 1. 使用@ManyToOne注解和@OneToMany注解来建立一对多关系。 ```java // 父表实体类 public class ParentEntity { // 父表主键 @TableId private Long id; // 父表其他属性 // 子表集合 @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL) private List<ChildEntity> children; // getter和setter方法 } // 子表实体类 public class ChildEntity { // 子表主键 @TableId private Long id; // 子表其他属性 // 父表外键 @ManyToOne @JoinColumn(name = "parent_id") private ParentEntity parent; // getter和setter方法 } ``` 2. 在Mapper接口中使用@Select注解来编写SQL查询语句。 ```java @Mapper public interface ParentMapper extends BaseMapper<ParentEntity> { @Select("SELECT * FROM parent WHERE id = #{id}") @Results({ @Result(property = "id", column = "id"), // 其他属性映射 @Result(property = "children", column = "id", many = @Many(select = "com.example.ChildMapper.getByParentId")) }) ParentEntity getById(Long id); } @Mapper public interface ChildMapper extends BaseMapper<ChildEntity> { @Select("SELECT * FROM child WHERE parent_id = #{parentId}") List<ChildEntity> getByParentId(Long parentId); } ``` 3. 在Service层调用Mapper接口的方法来实现查询。 ```java @Service public class ParentService { @Autowired private ParentMapper parentMapper; public ParentEntity getById(Long id) { return parentMapper.getById(id); } } ``` 通过以上步骤,你可以实现MyBatis Plus的多表一对多查询。你可以根据具体的业务需求来调整实体类和SQL查询语句

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱游泳的老白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值