文章目录

  • 前言
  • 环境
  • 项目搭建
  • 依赖引入
  • 配置文件编写
  • 一对一测试(association )
  • 测试1
  • null问题解决
  • 一对多测试(collection)
  • 总结
  • 资料参考
  • 代码参考
  • 2022.06.09 关于一对多查询效率问题


前言

写这篇博客之前呢,查看了下已经写过的博客,感觉针对于Mybatis的使用都太过简单。总觉得少了点什么,于是决定补充一下Mybatis中关联查询时,ResultMap配置写一对一一对多的使用案例。

环境

本次测试采取Springboot 2.1.4.RELEASE结合mybatis 1.3.0进行测试。

项目搭建

依赖引入

主要引入Springboot 2.1.4.RELEASEmybatis 1.3.0。具体如下所示:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

<properties>
    <java.version>1.8</java.version>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- 1.配置druid数据库连接池 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <!-- <version>5.0.4</version> -->
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.16</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <!-- 2.导入mybatis库文件 -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.0</version>
    </dependency>
    <!-- 使用aop拦截器 需要的aop库文件 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-core</artifactId>
    </dependency>
    <!-- 阿里巴巴json -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.44</version>
    </dependency>
    <!-- lombok 引入 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.20</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <!-- 打JAR包,不包含依赖文件;显式剔除配置文件 -->
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <configuration>
                <!-- 剔除配置文件 -->
                <!-- <excludes> <exclude>*.properties</exclude> <exclude>*.yml</exclude>
                    <exclude>*.xml</exclude> <exclude>*.txt</exclude> </excludes> -->
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <!-- MANIFEST.MF 中 Class-Path 各个依赖加入前缀 -->
                        <!--lib文件夹内容,需要 maven-dependency-plugin插件补充 -->
                        <classpathPrefix>lib/</classpathPrefix>
                        <!-- jar包不包含唯一版本标识 -->
                        <useUniqueVersions>false</useUniqueVersions>
                        <!--指定入口类 -->
                        <mainClass>cn.linkpower.dtuAuthPlatform.DtuAuthPlatformApplication</mainClass>
                    </manifest>

                </archive>
            </configuration>
        </plugin>
        <!-- 复制依赖的jar包到指定的文件夹里 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/lib</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <!-- 跳过单元测试 -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <skipTests>true</skipTests>
            </configuration>
        </plugin>
        <!--<plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>-->
    </plugins>
</build>

配置文件编写

配置application.yml,指定数据库连接配置,以及配置mybatis。

#mybatis 配置
mybatis:
  config-location: classpath:mybatis/mybatis-config.xml
  mapper-locations: classpath:mybatis/test/*.xml
  type-aliases-package: cn.linkpower.dtuAuthPlatform.vo



spring:
  datasource:
    url: jdbc:mysql://192.168.99.100:3306/mybatis_test?serverTimezone=UTC&useSSL=false&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource #当前数据源的操作类型
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    filters: stat,wall
    #配置初始化大小/最小/最大
    initial-size: 5
    min-idle: 5
    max-active: 20
    #获取连接等待超时时间
    max-wait: 60000
    #间隔多久进行一次检测,检测需要关闭的空闲连接
    time-between-eviction-runs-millis: 60000
    #一个连接在池中最小生存的时间
    min-evictable-idle-time-millis: 300000
    #validation-query: SELECT 'x'
    validation-query: SELECT 1 FROM DUAL
    test-while-idle: true
    test-on-borrow: false
    test-on-return: false
    #打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
    pool-prepared-statements: false
    max-pool-prepared-statement-per-connection-size: 20

一对一测试(association )

测试1

测试一对一的实现,先需要进行数据库表的创建和数据的导入,具体SQL如下所示:

CREATE TABLE tb_person (
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR ( 32 ),
	age INT,
	sex VARCHAR ( 8 ),
	card_id INT UNIQUE
);
INSERT INTO tb_person(name,age,sex,card_id) VALUES('zhangsan',29,'女',1);
INSERT INTO tb_person(name,age,sex,card_id) VALUES('lisi',29,'男',2);



CREATE TABLE tb_idcard (
	id INT PRIMARY KEY AUTO_INCREMENT,
	sfzh VARCHAR ( 18 ),
    bfjg VARCHAR ( 50 ));
INSERT INTO tb_idcard(sfzh,bfjg) VALUES('1234567894561333','东昌府公安局');
INSERT INTO tb_idcard(sfzh,bfjg) VALUES('5548465132151845','东昌府公安局');

由于是参考Mybatis的关联映射(一对一 一对多 多对多)进行设定,
对其中的部分注重点进行额外说明,此处数据引入以逆风微笑的李同学设定为主。

数据库中数据表创建并填充数据后,就需要编写pojo类。

import lombok.Data;

@Data
public class Tb_person {
    private Integer id;
    private String name;
    private Integer age;
    private String sex;
    private Tb_idcard tb_idcard;  //主键所在类的对象

    @Override
    public String toString() {
        return "Tb_person:{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", tb_idcard=" + tb_idcard +
                '}';
    }
}
import lombok.Data;

@Data
public class Tb_idcard {
    private Integer id;
    private String  sfzh;
    private String bfjg;
}

编写Mapper接口

import cn.mybatis.vo.Tb_person;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface UserMapper {
    List<Tb_person> findAll();
}

编写与UserMapper.java类对应的UserMapper.xml文件。

<?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">

<!-- parameterType 输入映射
     resultType 和 resultMap 完成输出映射-->
<!-- 与接口相关联-->
<mapper namespace="cn.mybatis.mapper.UserMapper">

    <resultMap id="findP_C" type="cn.mybatis.vo.Tb_person">
        <!-- id:你返回的主键-->
        <!-- column: 指定表中对应的字段-->
        <!-- property: 指定映射到的实体类对象属性-->
        <id column="id" property="id"></id>
        <result column="name" property="name"></result>
        <result column="age" property="age"></result>
        <result column="sex" property="sex"></result>
        <!--注意 :javaType 指定映射到实体对象属性的类型 -->
        <association property="tb_idcard" javaType="cn.mybatis.vo.Tb_idcard">
        	
            <id column="card_id" property="id"></id>
            <result column="sfzh" property="sfzh"></result>
            <result column="bfjg" property="bfjg"></result>
        </association>
    </resultMap>
    <select id="findAll" resultMap="findP_C">
        SELECT
           tp.`name`,
           tp.age,
           tp.sex,
           ti.id,
           ti.sfzh,
           ti.bfjg
        FROM
           tb_person tp,
           tb_idcard ti
        WHERE
           tp.card_id = ti.id
    </select>
</mapper>

针对UserMapper.java类,创建测试类进行测试运行:

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class UserMapperTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void getUserInfo(){
        List<Tb_person> all = userMapper.findAll();
        log.info(all.toString());
        log.info("==============");
        all.forEach(e->{
            String name = e.getName();
            Tb_idcard tb_idcard = e.getTb_idcard();
            String bfjg = tb_idcard.getBfjg();
            log.info("{},{}",name,bfjg);
        });
    }
}

执行后,控制台打印日志信息如下所示:

Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2f66f0d0] was not registered for synchronization because synchronization is not active
2022-02-20 17:38:40.445  INFO 46536 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@435e60ff] will not be managed by Spring
==>  Preparing: SELECT tp.`name`, tp.age, tp.sex, ti.id, ti.sfzh, ti.bfjg FROM tb_person tp, tb_idcard ti WHERE tp.card_id = ti.id 
==> Parameters: 
<==    Columns: name, age, sex, id, sfzh, bfjg
<==        Row: zhangsan, 29, 女, 1, 1234567894561333, 东昌府公安局
<==        Row: lisi, 29, 男, 2, 5548465132151845, 东昌府公安局
<==      Total: 2
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2f66f0d0]

2022-02-20 17:38:40.581  INFO 46536 --- [           main] cn.mybatis.mapper.UserMapperTest         : [Tb_person:{id=1, name='zhangsan', age=29, sex='女', tb_idcard=Tb_idcard(id=null, sfzh=1234567894561333, bfjg=东昌府公安局)}, Tb_person:{id=2, name='lisi', age=29, sex='男', tb_idcard=Tb_idcard(id=null, sfzh=5548465132151845, bfjg=东昌府公安局)}]
2022-02-20 17:38:40.581  INFO 46536 --- [           main] cn.mybatis.mapper.UserMapperTest         : ==============
2022-02-20 17:38:40.581  INFO 46536 --- [           main] cn.mybatis.mapper.UserMapperTest         : zhangsan,东昌府公安局
2022-02-20 17:38:40.582  INFO 46536 --- [           main] cn.mybatis.mapper.UserMapperTest         : lisi,东昌府公安局

细心的人可以发现,关于数据的查询结果,其中Tb_person.java类中,存在private Tb_idcard tb_idcard 属性。

此处是一对一关系,即:每个人只会有一串身份证号。
所以使用到<association>标签配置关联。

但是根据控制台中数据的打印结果,难免会发现与之对应的数据存在null的问题。

springboot实现一对多原理 springboot一对多查询_spring boot

null问题解决

SQL执行是查询到了数据信息,出现上述null的问题,根源在于映射关联的问题

<id>标签,表明每张表的主键
column参数,通常为数据库的对应的列名但可以是别名!property参数,表示映射的实体类的类型。
<result>标签,用于配置非主键的其他列对应信息。

参考:mybatis中collection标签中各属性的说明

根据上面的分析,可以将<resultMap>进行修改。修改后如下所示:

<resultMap id="findP_C" type="cn.mybatis.vo.Tb_person">
   <!-- id:你返回的主键-->
    <!-- column: 指定表中对应的字段-->
    <!-- property: 指定映射到的实体类对象属性-->
    <id column="id" property="id"></id>
    <result column="name" property="name"></result>
    <result column="age" property="age"></result>
    <result column="sex" property="sex"></result>
    <!--注意 :javaType 指定映射到实体对象属性的类型 -->
    <association property="tb_idcard" javaType="cn.mybatis.vo.Tb_idcard">

        <!--column 标签可以配置数据库表的列名,也可以配置别名-->
        <id column="card_id" property="id"></id>
        <result column="sfzh" property="sfzh"></result>
        <result column="bfjg" property="bfjg"></result>
    </association>
</resultMap>
<select id="findAll" resultMap="findP_C">
    SELECT
       tp.id id,
       tp.`name`,
       tp.age,
       tp.sex,
       ti.id as card_id, /*注意这里的别名,与association标签中的id标签中column对应*/
       ti.sfzh,
       ti.bfjg
    FROM
       tb_person tp,
       tb_idcard ti
    WHERE
       tp.card_id = ti.id
</select>

column可以是数据库表的列名,也可以是sql 查询后的 字段别名

再次执行上面的测试代码,运行后控制台数据如下所示:

springboot实现一对多原理 springboot一对多查询_springboot实现一对多原理_02


能达到映射关系的对应!

一对多测试(collection)

每个人都可能会有多个订单信息。

首先还是先创建数据库的表,同时向其中导入相关的测试数据:

CREATE TABLE tb_user(
    id INT(32) PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(32),
    address VARCHAR(256)
);
INSERT INTO tb_user VALUES( 1, '张三', '山东聊城' );
INSERT INTO tb_user VALUES( 2, '李四', '山东济南' );
INSERT INTO tb_user VALUES( 3, '王五', '山东青岛' );

CREATE TABLE tb_orders (
	id INT ( 32 ) PRIMARY KEY AUTO_INCREMENT,
	number VARCHAR ( 32 ) NOT NULL,
	user_id INT ( 32 ) NOT NULL,
FOREIGN KEY ( user_id ) REFERENCES tb_user ( id ));
INSERT INTO tb_orders VALUES(1,'20190402001',1);
INSERT INTO tb_orders VALUES(2,'20190402002',2);
INSERT INTO tb_orders VALUES(3,'20190402003',1);

编写pojo实体类,与对应的数据库表进行对应。

import lombok.Data;
import java.util.List;

@Data
public class Tb_user {
    private Integer userId;
    private String username;
    private String address;
    private List<Tb_orders> tb_ordersList;

    @Override
    public String toString() {
        return "Tb_user:{" +
                "userId=" + userId +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", tb_ordersList:" + tb_ordersList +
                '}';
    }
}
import lombok.Data;

@Data
public class Tb_orders {

    private Integer orderId;
    private Integer userId;
    private String number;

    @Override
    public String toString() {
        return "Tb_orders{" +
                "orderId=" + orderId +
                ", userId=" + userId +
                ", number='" + number + '\'' +
                '}';
    }
}

编写对应的OrdersMapper.java接口。

import cn.mybatis.vo.two.Tb_user;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface OrdersMapper {
    List<Tb_user> findAll();
}

针对OrdersMapper.java接口,编写对应的OrdersMapper.xml配置文件:

首先需要保证pojo类,与数据库中各个表的列名称进行对应。
其次,编写指定的resultMap,封装映射结果集。

<?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">

<!-- parameterType 输入映射
     resultType 和 resultMap 完成输出映射-->
<!-- 与接口相关联-->
<mapper namespace="cn.mybatis.mapper.two.OrdersMapper">

    <resultMap id="findU_O" type="cn.mybatis.vo.two.Tb_user">
    	<!--column 标签可以配置数据库表的列名,也可以配置别名-->
        <id column="id" property="userId"></id>
        <result column="username" property="username"></result>
        <result column="address" property="address"></result>
        <collection property="tb_ordersList" ofType="cn.mybatis.vo.two.Tb_orders">

            <!--与具体的sql相关联,根据编写的sql返回结果集的key,进行解析;
                如果column 设定的是数据库的列名,但是查询结果使用了as进行了指向,则不能获取到对应的数据;
                即:设定了as别名,此处column则需要设定为别名称-->
            <id column="orderId" property="orderId"></id>
            <result column="userId" property="userId"></result>
            <result column="number" property="number"></result>
        </collection>
    </resultMap>

    <!--<resultMap id="findU_O" type="cn.mybatis.vo.two.Tb_user">
        <id column="id" property="userId"></id>
        <result column="username" property="username"></result>
        <result column="address" property="address"></result>
        <collection property="tb_ordersList" ofType="cn.mybatis.vo.two.Tb_orders">
            <id column="id" property="orderId"></id>
            <result column="number" property="number"></result>
            <result column="user_id" property="userId"></result>
        </collection>
    </resultMap>-->

    <select id="findAll" resultMap="findU_O">
		SELECT
		   tu.id,
		   tu.username,
		   tu.address,
		   tbo.user_id userId,
		   tbo.id orderId,
		   tbo.number
		FROM
		   tb_user tu,
		   tb_orders tbo
		WHERE
		   tu.id = tbo.user_id
   </select>
</mapper>

注意点:

column 标签可以配置数据库表的列名,也可以配置别名

所以,在上述的<resultMap>中,配置的<collection>标签中的各个字段类型的column字段,是根据SQL查询返回名称进行配置!

编写OrdersMapper.java的测试类,如下所示:

package cn.mybatis.mapper.two;

import cn.mybatis.vo.Tb_idcard;
import cn.mybatis.vo.Tb_person;
import cn.mybatis.vo.two.Tb_user;
import junit.framework.TestCase;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class OrdersMapperTest {

    @Autowired
    private OrdersMapper ordersMapper;

    @Test
    public void getOrderInfo(){
        List<Tb_user> all = ordersMapper.findAll();
        log.info(all.toString());
        log.info("=====================");
        all.forEach(e->{
            Tb_user e1 = e;
            log.info(e1.toString());
        });
    }
}

运行测试代码,查看控制台的测试结果:

2022-02-20 18:03:52.117  INFO 46932 --- [           main] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5ba26eb0] will not be managed by Spring
==>  Preparing: SELECT tu.id, tu.username, tu.address, tbo.user_id userId, tbo.id orderId, tbo.number FROM tb_user tu, tb_orders tbo WHERE tu.id = tbo.user_id 
==> Parameters: 
<==    Columns: id, username, address, userId, orderId, number
<==        Row: 1, 张三, 山东聊城, 1, 1, 20190402001
<==        Row: 1, 张三, 山东聊城, 1, 3, 20190402003
<==        Row: 2, 李四, 山东济南, 2, 2, 20190402002
<==      Total: 3
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6fc29daa]

2022-02-20 18:03:52.262  INFO 46932 --- [           main] cn.mybatis.mapper.two.OrdersMapperTest   : [Tb_user:{userId=1, username='张三', address='山东聊城', tb_ordersList:[Tb_orders{orderId=1, userId=1, number='20190402001'}, Tb_orders{orderId=3, userId=1, number='20190402003'}]}, Tb_user:{userId=2, username='李四', address='山东济南', tb_ordersList:[Tb_orders{orderId=2, userId=2, number='20190402002'}]}]
2022-02-20 18:03:52.262  INFO 46932 --- [           main] cn.mybatis.mapper.two.OrdersMapperTest   : =====================
2022-02-20 18:03:52.263  INFO 46932 --- [           main] cn.mybatis.mapper.two.OrdersMapperTest   : Tb_user:{userId=1, username='张三', address='山东聊城', tb_ordersList:[Tb_orders{orderId=1, userId=1, number='20190402001'}, Tb_orders{orderId=3, userId=1, number='20190402003'}]}
2022-02-20 18:03:52.263  INFO 46932 --- [           main] cn.mybatis.mapper.two.OrdersMapperTest   : Tb_user:{userId=2, username='李四', address='山东济南', tb_ordersList:[Tb_orders{orderId=2, userId=2, number='20190402002'}]}

springboot实现一对多原理 springboot一对多查询_spring_03

数据能够一一对应!

总结

针对本次测试和观察问题现象,对其做出以下几点总结

  • 1、column 标签可以配置数据库表的列名,也可以配置别名。

根据指定的SQL返回结果集映射类型定义。

  • 2、如果需要返回类似JSON的数据格式,需要重写 toString(),让其返回样式和JSON一致即可。

资料参考

Mybatis的关联映射(一对一 一对多 多对多)

代码参考

gitee 代码仓库

2022.06.09 关于一对多查询效率问题

mybatis处理一对多时性能太低的解决方法探讨

类似的,查询出的数据取消映射,可以借鉴如下方式进行数据转换。

public class Test2 {
    public static void main(String[] args) {
        List<TestVo> userList = new ArrayList<>();
        TestVo obj1 = new TestVo();
        obj1.setMonth("一月");
        obj1.setName("xj1");
        obj1.setAge(BigDecimal.TEN);
        userList.add(obj1);

        TestVo obj2 = new TestVo();
        obj2.setMonth("一月");
        obj2.setName("xj11");
        obj2.setAge(BigDecimal.TEN);
        userList.add(obj2);

        TestVo obj3 = new TestVo();
        obj3.setMonth("一月");
        obj3.setName("xj111");
        obj3.setAge(BigDecimal.TEN);
        userList.add(obj3);

        TestVo obj4 = new TestVo();
        obj4.setMonth("二月");
        obj4.setName("xj2");
        obj4.setAge(BigDecimal.TEN);
        userList.add(obj4);

        //System.out.println( userList.toString());

        Map<String, List<TestVo>> collect = userList.stream().collect(Collectors.groupingBy(TestVo::getMonth));

        for (Map.Entry<String, List<TestVo>> stringListEntry : collect.entrySet()) {
            System.out.println(stringListEntry.getKey() + " === "+stringListEntry.getValue());
            List<TestVo> value = stringListEntry.getValue();
            value.forEach(e->{
                System.out.println(" ----- >"+e);
            });
        }
        //System.out.println(collect);
    }
}

主要方式为:

Map<String, List<TestVo>> collect = userList.stream()
.collect(Collectors.groupingBy(TestVo::getMonth));

springboot实现一对多原理 springboot一对多查询_spring boot_04