在 springBoot 的 web 项目中,平时查询数据返回都是:集合 list 、实体类 bean 、数量 int / long 。如果返回 map ,也是Map<String,Map<String,Object>> 或Map<String,实体类bean> ,直接返回Map<String,String> 或Map<String,Object> 会报错,通过查询得知,可以使用 ResultHandler 实现。 ResultHandler,对返回的结果进行处理,最终得到自己想要的数据格式或类型。就是说,可以自定义返回类型。
参考
mybatis之ResultHandler如何使用 Spring Boot 下 MyBatis 的几种使用方式 mybatis中resulthandler的用法 Mybatis源码分析:MapperMethod中内部静态类MethodSignature的作用 springboot整合Mybatis 之 创建对象SqlSessionFactory和SqlSession(一) Mybatis select返回值为map时,选取表字段的两列作为key,value mybatis 中的 ResultHandler(传入map返回map) MyBatis 核心配置综述之 ResultSetHandler
操作
代码
pom
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.1.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.3.8</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
配置
server:
port: 8080
spring:
datasource:
password: 'xxxxxx'
username: 'root'
driver-class-name: com.mysql.cj.jdbc.Driver
url: 'jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai'
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: xx.xxx.pojo
configuration:
map-underscore-to-camel-case: true
java
启动类增加mybatis的扫描注解@MapperScan(basePackages = "xx.xxx.dao")
controller
import xx.xxx.common.MapResultHandler;
import xx.xxx.dao.DicTypeDao;
import xx.xxx.pojo.DicType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
@Controller
@RequestMapping("/db")
public class TestDbController {
private final Logger logger = LoggerFactory.getLogger(TestDbController.class);
@Resource private DicTypeDao dicTypeDao;
@RequestMapping("")
@ResponseBody
public String root(){
return "Hello Spring Boot DB demo.";
}
@RequestMapping("/all")
@ResponseBody
public List<DicType> all(){
logger.info("查询 全部 数据");
return dicTypeDao.selectAll();
}
@RequestMapping("/map")
@ResponseBody
public Map<String, Object> map(){
logger.info("查询 全部 数据");
MapResultHandler handler = new MapResultHandler("DIC_TYPE","DIC_NAME");
dicTypeDao.selectMap(handler);
Map<String, Object> map = handler.getResultMap();
logger.info("map >> {}",map);
return map;
}
}
dao
import xx.xxx.DicType;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.ResultType;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.session.ResultHandler;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Map;
/**
* DicTypeDao 接口说明:
*
* @author z.y.l
* @version v1.0
* @date 2022/10/9
*/
@Mapper
@Repository
public interface DicTypeDao {
/**
* 查询全部
* @return 集合
*/
List<DicType> selectAll();
/**
* 查询结果map
* @param handler 自定义结果处理器
*/
@ResultType(Map.class)
@Select("select DIC_TYPE, DIC_NAME from test.dic_type_b")
void selectMap(ResultHandler<Map<String, Object>> handler);
}
测试表
import java.util.Date;
/**
* 字典类型表
* <pre>
* CREATE TABLE `dic_type_b` (
* `ID` int NOT NULL COMMENT '主键',
* `DIC_TYPE` varchar(10) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型',
* `DIC_NAME` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型名称',
* `VALID` varchar(2) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '有效标志',
* `MEMO` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '字典类型说明',
* `CREATE_TIME` datetime DEFAULT NULL COMMENT '新增时间',
* `UPDATE_TIME` datetime DEFAULT NULL COMMENT '修改时间',
* `VERSION` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '版本',
* PRIMARY KEY (`ID`)
* ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='字典类型表';
* </pre>
* @author z.y.l
* @version v1.0
* @date 2022/10/9
*/
public class DicType {
private Integer id;
private String dicType;
private String dicName;
private String valid;
private String memo;
private Date createTime;
private Date updateTime;
private String version;
set...
get...
}
mapper
<?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="xx.xxx.dao.DicTypeDao">
<select id="selectAll" resultType="xx.xxx.pojo.DicType">
select ID, DIC_TYPE, DIC_NAME,VALID,MEMO,CREATE_TIME,UPDATE_TIME,VERSION
from test.dic_type_b
</select>
</mapper>
自定义ResultHandler
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
public class MapResultHandler implements ResultHandler<Map<String, Object>> {
private final Logger logger = LoggerFactory.getLogger(MapResultHandler.class);
private final Map<String, Object> resultMap = new HashMap<>();
private final String key,val;
@Override
public void handleResult(ResultContext<? extends Map<String, Object>> resultContext) {
Map<String, Object> map = resultContext.getResultObject();
logger.debug("自定义查询处理器-行-map,{}",map);
Object value = map.get(key);
if (null != value && !resultMap.containsKey(value.toString())) {
resultMap.put(map.get(key).toString(),map.get(val));
}else{
logger.debug("自定义查询处理器-行异常-map,{}",value);
}
}
public MapResultHandler(String key,String val){
this.key = key;
this.val = val;
}
public Map<String, Object> getResultMap() {
return resultMap;
}
}
测试
访问:http://localhost:8080/db/map 
{
"STATUS": "状态",
"GEND": "性别",
"TYPE": "类型"
}
日志
TestDbController [TestDbController.java:46] =.= map >> {STATUS=状态, GEND=性别, TYPE=类型}
其他
通过查询的网络文章得知,执行自定义ResultHandler 的代码位置。org.apache.ibatis.binding.MapperMethod 类方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
自定义 DefaultSqlSession 未成功实现
再看了 mybatis 的查询返回 map 源码(太菜买看完),如果要想通过扩展 mybatis 来支持Map<String,String> ,但是因为部分核心bean(org.apache.ibatis.mapping.ResultMap )不太好处理内部字段,故放弃。 本标题只做记录,可以不用看。 重写的类有: org.apache.ibatis.session.defaults.DefaultSqlSessionFactory 方法SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) ; org.apache.ibatis.session.defaults.DefaultSqlSessio 方法SqlSession <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) ; org.apache.ibatis.executor.result.DefaultMapResultHandler 方法void handleResult(ResultContext<? extends V> context) ;
END
|