准备系列-Mybatis(七) TKmybatis-springboot 项目 多种查询方式及结果集映射Map结构实现

99 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 7 天,点击查看活动详情

在上面的文章中,我们可以看到 我们采用了几种实现方式查询,结果都可以正确的实现 下面我们把业务实现逻辑及核心代码展示, 这样我们在使用mybatis的时候才能得心应手,不管是Example 还是Criteria 还是Weekend 或者是 注解结合XML的实现方式, 我们都能应付自如

本文 我们着重介绍下 mybatis如何映射Map结构结果实现

查询逻辑的业务

1.1 application.properties配置
server.port=8800
        
spring.datasource.druid.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.max-active=20
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.min-evictable-idle-time-millis=300000
spring.datasource.druid.max-wait=60000
spring.datasource.druid.validation-query=select 1
spring.datasource.druid.test-on-borrow=false
spring.datasource.druid.test-on-return=false
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.time-between-eviction-runs-millis=60000
        
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.mapper-locations=classpath:sqlmapper/*Mapper.xml

2.映射结果集Map结构实现

文档对resultType属性的表述:resultType结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。

2.1 单个对象Map结构返回

如果需要把所有的字段 全部都转化成Map结构

  • Map的key 就是 column字段名字比如 "user_id", "address", "is_del", 区分大小写及下划线
  • value 就是返回的对象object 就是自己定义的UserInfoPO的字段的value信息

Mapper文件

Map<String, Object> map1User(@Param(value = "userid") String userid);

XML文件, 在XML中定义 SQL语句 如下:

<!-- 单个字段转化为Map结构 -->
<select id="map1User" resultType="map">
  select * from user_info where user_id = #{userid}
</select>

查询结果如下:

image.png

image.png

2.2 多条数据如果用上面的查询会出现什么问题?

如果查到的是多个对象 现在数据有 两条数据 user_id= 456的 记录, 我们看下如果 用上面的map结构会出现什么问题

image.png

报错信息如下:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; 
nested exception is org.apache.ibatis.exceptions.TooManyResultsException:
Expected one result (or null) to be returned by selectOne(), but found: 2] with root cause

org.apache.ibatis.exceptions.TooManyResultsException: 
Expected one result (or null) to be returned by selectOne(), but found: 2

image.png

2.3 多个UserInfo 指定Key 结果返回

多个UserInfo Map集合返回,指定Key字段 上面的情况我们只是记录了一条数据并返回了Map, 如果我们想查询多个用户数据, 全都指定了字段作为 返回结果的Key应该如何处理 ?

通过使用@MapKey注解来解决上面的问题

先看下官方文档中对于@Mapkey的表述:

这是一个用在返回值为 Map 的方法上的注解。它能够将存放对象的 List 转化为 key 值为对象的某一属性的 Map。属性有: value,填入的是对象的属性名,作为 Map 的 key 值。

我们可以使用@Mapkey注解指定封装map时使用对象的哪个属性来作为key,通常我们使用主键就可以,或者唯一索引,确保key不会重复。

@MapKey("user_id")
Map<String, UserInfoPO> nameUserMap();

XML文件实现

<!--  指定Key的多个Map-->
<select id="nameUserMap" resultType="java.util.Map">
  select * from user_info
</select>

查询结果如下: 可以看到 查询结果 14条记录, 结果Map中只有13条, 有一个user_id是重复的 456 Map中取的是 后面的 nn替换掉了前面的mm 记录

image.png

image.png

2.3 查询个别字段映射到新的PO

如果我现在只需要 2个字段 比如我只需要 id和name, 应该如何处理? 我们需要新建一个 对象, 制定resultType类型即可

比如我们需要 city及count信息, 只需要 新建PO实体类UserCity信息, 对应的就是 city字段及count字段信息

public class UserCity {
    
    private String city;
    private String count;

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getCount() {
        return count;
    }

    public void setCount(String count) {
        this.count = count;
    }
}

然后 定义XML文件及Mapper文件

Mapper文件
List<UserCity> countUser(@Param("age") Integer age, @Param("names") List<String> names);

XML文件
<select id="countUser" resultType="com.jzj.tdmybatis.domain.po.UserCity">
        select address as city , count(*) as count from user_info
        where user_name in
        <foreach close=")" collection="names" item="iterm" open="(" separator=",">
            #{iterm}
        </foreach>
        group by city
    </select>

然后即可实现字段映射到UserCity上

3 业务Service及Controller逻辑

3.1 TestController 测试API

采用 6 种方式 实现了 mybatis的查询逻辑

package com.jzj.tdmybatis.controller;

import cn.hutool.json.JSONUtil;
import com.jzj.tdmybatis.domain.po.UserCity;
import com.jzj.tdmybatis.domain.po.UserInfoPO;
import com.jzj.tdmybatis.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @ClassName TestController
 * @Description
 * @Author jiazijie
 * @Date 2023/2/2 22:05
 */
@RestController
@Slf4j
public class TestController {


    @Resource
    private IUserService userService;

    /**
     * 探活接口
     */
    @RequestMapping("/temp/ping")
    @ResponseBody
    public Object ping() {

        return "pong";
    }

    /**
     * 查询user接口
     */
    @RequestMapping("/temp/user")
    @ResponseBody
    public Object user(String id) {
        //测试重复的
        UserInfoPO user = userService.getUser(id);
        log.info("user====", JSONUtil.toJsonStr(user));
        return user;
    }


    /**
     * 查询user接口
     */
    @RequestMapping("/temp/query1")
    @ResponseBody
    public Object query1() {
        //测试注解信息
        // 找 年龄 > 30岁的员工
        List<UserInfoPO> userInfoPOS = userService.selectByAge(30);
        log.info("query1====", JSONUtil.toJsonStr(userInfoPOS));
        return userInfoPOS;
    }

    /**
     * 查询user接口
     */
    @RequestMapping("/temp/query2")
    @ResponseBody
    public Object query2() {
        //测试XML信息
        // 找 年龄 > 30岁的员工 且 名字是 aa或者bb, cc的员工

        List<String> names = new ArrayList<>();
        names.add("aa");
        names.add("bb");
        names.add("cc");
        List<UserCity> userInfoPOS = userService.countUser(30, names);
        log.info("query2====", JSONUtil.toJsonStr(userInfoPOS));
        return userInfoPOS;
    }

    /**
     * 查询user接口
     */
    @RequestMapping("/temp/query3")
    @ResponseBody
    public Object query3() {
        //测试XML信息
        // 找 年龄 > 30岁的员工 且 名字是 aa或者bb, cc的员工

        List<String> names = new ArrayList<>();
        names.add("aa");
        names.add("bb");
        names.add("cc");
        List<UserInfoPO> userInfoPOS = userService.getUserExample(1, names);
        log.info("query3====", JSONUtil.toJsonStr(userInfoPOS));
        return userInfoPOS;
    }


    /**
     * 查询user接口
     */
    @RequestMapping("/temp/query4")
    @ResponseBody
    public Object query4() {
        //测试Example信息
        // 找 年龄 > 30岁的员工 且 名字是 aa或者bb, cc的员工
        List<String> names = new ArrayList<>();
        names.add("aa");
        names.add("bb");
        names.add("cc");
        List<UserInfoPO> userExample = userService.getUserExample(1, names);
        log.info("query4====", JSONUtil.toJsonStr(userExample));
        return userExample;
    }


    /**
     * 查询user接口
     */
    @RequestMapping("/temp/query5")
    @ResponseBody
    public Object query5() {
        //测试Builder 信息
        // 找 年龄 > 30岁的员工 且 名字是 aa或者bb, cc的员工

        List<String> names = new ArrayList<>();
        names.add("aa");
        names.add("bb");
        names.add("cc");
        List<UserInfoPO> userBuilder = userService.getUserBuilder(1, names);
        log.info("query5====", JSONUtil.toJsonStr(userBuilder));
        return userBuilder;
    }


    /**
     * 查询user接口
     */
    @RequestMapping("/temp/query6")
    @ResponseBody
    public Object query6() {
        //测试Weekend 信息
        // 找 年龄 > 30岁的员工 且 名字是 aa或者bb, cc的员工

        List<String> names = new ArrayList<>();
        names.add("aa");
        names.add("bb");
        names.add("cc");
        List<UserInfoPO> userWeekend = userService.getUserWeekend(30, names);
        log.info("query6====", JSONUtil.toJsonStr(userWeekend));
        return userWeekend;
    }
    
/**
 * 探活接口
 */
@RequestMapping("/temp/map1")
@ResponseBody
public Object map1(String uid) {


    Map<String, Object> usermap = userService.map1User(uid);

    //使用Mybatis返回Map结构时字段别名需要用双引号包裹否则别名会全部大写或小写
    log.info("key:user_id value:"+usermap.get("user_id"));
    log.info("usermap=="+usermap);
    return "pong";
}

/**
 * 探活接口
 */
@RequestMapping("/temp/map2")
@ResponseBody
public Object map2(String uid) {


    Map<String, UserInfoPO> usermap = userService.nameUserMap();
    log.info("usermap=="+JSONUtil.toJsonStr(usermap));
    return "pong";
}

}
1.3 UserMapper mapper文件的实现
package com.jzj.tdmybatis.repository.mapper;

import com.jzj.tdmybatis.config.BaseMapper;
import com.jzj.tdmybatis.domain.po.UserCity;
import com.jzj.tdmybatis.domain.po.UserInfoPO;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

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

public interface UserInfoMapper extends BaseMapper<UserInfoPO> {

    @Select("select * from user_info where age > #{age}")
    List<UserInfoPO> selectGreaterAge(@Param("age") Integer age);


    List<UserCity> countUser(@Param("age") Integer age, @Param("names") List<String> names);

    Map<String, Object> map1User(@Param(value = "userid") String userid);


    @MapKey("user_id")
    Map<String, UserInfoPO> nameUserMap();

}
1.4 XML 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">
<mapper namespace="com.jzj.tdmybatis.repository.mapper.UserInfoMapper">
    <resultMap id="BaseResultMap" type="com.jzj.tdmybatis.domain.po.UserInfoPO">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="id" jdbcType="BIGINT" property="id"/>
        <result column="user_id" jdbcType="CHAR" property="userId"/>
        <result column="user_name" jdbcType="CHAR" property="userName"/>
        <result column="age" jdbcType="INTEGER" property="age"/>
        <result column="address" jdbcType="VARCHAR" property="address"/>
        <result column="order_ids" jdbcType="CHAR" property="orderIds"/>
        <result column="goods" jdbcType="CHAR" property="goods"/>
        <result column="sort_order" jdbcType="INTEGER" property="sortOrder"/>
        <result column="is_del" jdbcType="TINYINT" property="isDel"/>
        <result column="is_del2" jdbcType="TINYINT" property="isDel2"/>
        <result column="addtime" jdbcType="BIGINT" property="addtime"/>
        <result column="modtime" jdbcType="BIGINT" property="modtime"/>
    </resultMap>

    <select id="countUser" resultType="com.jzj.tdmybatis.domain.po.UserCity">
        select address as city , count(*) as count from user_info
        where user_name in
        <foreach close=")" collection="names" item="iterm" open="(" separator=",">
            #{iterm}
        </foreach>
        group by city
    </select>


    <select id="map1User" resultType="map">
      select * from user_info where user_id = #{userid}
    </select>

    <!--  指定Key的多个Map-->
    <select id="nameUserMap" resultType="java.util.Map">
      select * from user_info
    </select>
</mapper>