重构 TableInfo.buildResultMap

This commit is contained in:
开源海哥 2023-06-13 13:01:40 +08:00
parent b67bd62a0e
commit 4cc7fdb4e7
6 changed files with 204 additions and 186 deletions

View File

@ -36,15 +36,16 @@ import org.apache.ibatis.executor.keygen.SelectKeyGenerator;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.*;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ResultMap;
import org.apache.ibatis.session.*;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.util.MapUtil;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -189,37 +190,11 @@ public class FlexConfiguration extends Configuration {
String resultMapId = tableInfo.getEntityClass().getName();
/*ResultMap resultMap;
ResultMap resultMap;
if (hasResultMap(resultMapId)) {
resultMap = getResultMap(resultMapId);
} else {
resultMap = tableInfo.buildResultMap(this);
this.addResultMap(resultMap);
}*/
// 变量名与属性名区分开
List<ResultMap> resultMapList;
if (hasResultMap(resultMapId)) {
resultMapList = new ArrayList<>();
fillResultMapList(resultMapId, resultMapList);
} else {
resultMapList = tableInfo.buildResultMapList(this);
for (ResultMap resultMap : resultMapList) {
if (!hasResultMap(resultMap.getId())) {
addResultMap(resultMap);
}
}
/*
* 这里解释一下为什么要反转这个集合
*
* MyBatis 在解析 ResultMaps 的时候是按照顺序一个一个进行解析的对于有嵌套
* ResultMap 对象也就是 nestResultMap 需要放在靠前的位置首先解析
*
* 而我们进行递归 buildResultMapList 是非嵌套 ResultMap 在集合最开始的位置
* 所以要反转一下集合 hasNestedResultMaps ResultMap 对象放到集合的最
* 前面
*/
Collections.reverse(resultMapList);
}
return new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), ms.getSqlSource(), ms.getSqlCommandType())
@ -234,8 +209,7 @@ public class FlexConfiguration extends Configuration {
.lang(ms.getLang())
.resultOrdered(ms.isResultOrdered())
.resultSets(ms.getResultSets() == null ? null : String.join(",", ms.getResultSets()))
//.resultMaps(CollectionUtil.newArrayList(resultMap)) // 替换resultMap
.resultMaps(resultMapList)
.resultMaps(Collections.singletonList(resultMap))
.resultSetType(ms.getResultSetType())
.flushCacheRequired(ms.isFlushCacheRequired())
.useCache(ms.isUseCache())
@ -243,19 +217,6 @@ public class FlexConfiguration extends Configuration {
.build();
}
private void fillResultMapList(String resultMapId, List<ResultMap> resultMapList) {
ResultMap resultMap = this.getResultMap(resultMapId);
resultMapList.add(resultMap);
if (resultMap.hasNestedResultMaps()) {
for (ResultMapping resultMapping : resultMap.getResultMappings()) {
String nestedResultMapId = resultMapping.getNestedResultMapId();
if (nestedResultMapId != null) {
fillResultMapList(nestedResultMapId, resultMapList);
}
}
}
}
/**
* 生成新的已替换主键生成器的 MappedStatement
*

View File

@ -706,80 +706,12 @@ public class TableInfo {
.collect(Collectors.toList());
}
/**
* @deprecated 该功能有更好的方式实现此方法可能会被移除
*/
@Deprecated
public ResultMap buildResultMap(Configuration configuration) {
String resultMapId = entityClass.getName();
List<ResultMapping> resultMappings = new ArrayList<>();
for (ColumnInfo columnInfo : columnInfoList) {
ResultMapping mapping = new ResultMapping.Builder(configuration, columnInfo.property,
columnInfo.column, columnInfo.propertyType)
.jdbcType(columnInfo.getJdbcType())
.typeHandler(columnInfo.buildTypeHandler())
.build();
resultMappings.add(mapping);
//add property mapper for sql: select xxx as property ...
if (!Objects.equals(columnInfo.getColumn(), columnInfo.getProperty())) {
ResultMapping propertyMapping = new ResultMapping.Builder(configuration, columnInfo.property,
columnInfo.property, columnInfo.propertyType)
.jdbcType(columnInfo.getJdbcType())
.typeHandler(columnInfo.buildTypeHandler())
.build();
resultMappings.add(propertyMapping);
if (configuration.hasResultMap(resultMapId)) {
return configuration.getResultMap(resultMapId);
}
}
for (IdInfo idInfo : primaryKeyList) {
ResultMapping mapping = new ResultMapping.Builder(configuration, idInfo.property,
idInfo.column, idInfo.propertyType)
.flags(CollectionUtil.newArrayList(ResultFlag.ID))
.jdbcType(idInfo.getJdbcType())
.typeHandler(idInfo.buildTypeHandler())
.build();
resultMappings.add(mapping);
}
if (joinTypes != null && !joinTypes.isEmpty()) {
joinTypes.forEach((fieldName, fieldType) -> {
TableInfo joinTableInfo = TableInfoFactory.ofEntityClass(fieldType);
List<ColumnInfo> joinTableInfoColumnInfoList = joinTableInfo.getColumnInfoList();
for (ColumnInfo joinColumnInfo : joinTableInfoColumnInfoList) {
if (!existColumn(resultMappings, joinColumnInfo.column)) {
ResultMapping mapping = new ResultMapping.Builder(configuration, fieldName + "." + joinColumnInfo.property,
joinColumnInfo.column, joinColumnInfo.propertyType)
.jdbcType(joinColumnInfo.jdbcType)
.typeHandler(joinColumnInfo.buildTypeHandler())
.build();
resultMappings.add(mapping);
}
//add property mapper for sql: select xxx as property ...
if (!existColumn(resultMappings, joinColumnInfo.property)) {
if (!Objects.equals(joinColumnInfo.column, joinColumnInfo.property)) {
ResultMapping propertyMapping = new ResultMapping.Builder(configuration, fieldName + "." + joinColumnInfo.property,
joinColumnInfo.property, joinColumnInfo.propertyType)
.jdbcType(joinColumnInfo.jdbcType)
.typeHandler(joinColumnInfo.buildTypeHandler())
.build();
resultMappings.add(propertyMapping);
}
}
}
});
}
return new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
}
public List<ResultMap> buildResultMapList(Configuration configuration) {
String resultMapId = entityClass.getName();
List<ResultMap> resultMaps = new ArrayList<>();
List<ResultMapping> resultMappings = new ArrayList<>();
// <resultMap> 标签下的 <result> 标签映射
@ -819,19 +751,11 @@ public class TableInfo {
// 获取嵌套类型的信息也就是 javaType 属性
TableInfo tableInfo = TableInfoFactory.ofEntityClass(fieldType);
// 构建嵌套类型的 ResultMap 对象也就是 <association> 标签下的内容
// 这里是递归调用直到嵌套类型里面没有其他嵌套类型或者集合类型为止
List<ResultMap> resultMapList = tableInfo.buildResultMapList(configuration);
// 寻找是否有嵌套 ResultMap 引用
Optional<ResultMap> nestedResultMap = resultMapList.stream()
.filter(e -> fieldType.getName().equals(e.getId()))
.findFirst();
// 处理嵌套类型 ResultMapping 引用
nestedResultMap.ifPresent(resultMap -> resultMappings.add(new ResultMapping.Builder(configuration, fieldName)
ResultMap nestedResultMap = tableInfo.buildResultMap(configuration);
resultMappings.add(new ResultMapping.Builder(configuration, fieldName)
.javaType(fieldType)
.nestedResultMapId(resultMap.getId())
.build()));
// 全部添加到 ResultMap 集合当中
resultMaps.addAll(resultMapList);
.nestedResultMapId(nestedResultMap.getId())
.build());
});
}
@ -841,34 +765,17 @@ public class TableInfo {
// 获取集合泛型类型的信息也就是 ofType 属性
TableInfo tableInfo = TableInfoFactory.ofEntityClass(genericClass);
// 构建嵌套类型的 ResultMap 对象也就是 <collection> 标签下的内容
// 这里是递归调用直到集合类型里面没有其他嵌套类型或者集合类型为止
List<ResultMap> resultMapList = tableInfo.buildResultMapList(configuration);
// 寻找是否有嵌套 ResultMap 引用
Optional<ResultMap> nestedResultMap = resultMapList.stream()
.filter(e -> genericClass.getName().equals(e.getId()))
.findFirst();
// 处理嵌套类型 ResultMapping 引用
nestedResultMap.ifPresent(resultMap -> resultMappings.add(new ResultMapping.Builder(configuration, field.getName())
ResultMap nestedResultMap = tableInfo.buildResultMap(configuration);
resultMappings.add(new ResultMapping.Builder(configuration, field.getName())
.javaType(field.getType())
.nestedResultMapId(resultMap.getId())
.build()));
// 全部添加到 ResultMap 集合当中
resultMaps.addAll(resultMapList);
.nestedResultMapId(nestedResultMap.getId())
.build());
});
}
resultMaps.add(new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build());
return resultMaps;
}
private static boolean existColumn(List<ResultMapping> resultMappings, String name) {
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getColumn().equalsIgnoreCase(name)) {
return true;
}
}
return false;
ResultMap resultMap = new ResultMap.Builder(configuration, resultMapId, entityClass, resultMappings).build();
configuration.addResultMap(resultMap);
return resultMap;
}

View File

@ -0,0 +1,104 @@
package com.mybatisflex.test;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.ColumnMask;
import com.mybatisflex.core.handler.Fastjson2TypeHandler;
import com.mybatisflex.core.mask.Masks;
import javax.validation.constraints.NotBlank;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class AccountDTO {
private Long id;
@ColumnMask(Masks.CHINESE_NAME)
private String userName;
private int age;
@NotBlank
private Date birthday;
@Column(typeHandler = Fastjson2TypeHandler.class, isLarge = true)
private Map<String, Object> options;
@Column(isLogicDelete = true)
private Boolean isDelete;
private List<Article> articles;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public Map<String, Object> getOptions() {
return options;
}
public void setOptions(Map<String, Object> options) {
this.options = options;
}
public void addOption(String key, Object value) {
if (options == null) {
options = new HashMap<>();
}
options.put(key, value);
}
public Boolean getDelete() {
return isDelete;
}
public void setDelete(Boolean delete) {
isDelete = delete;
}
public List<Article> getArticles() {
return articles;
}
public void setArticles(List<Article> articles) {
this.articles = articles;
}
@Override
public String toString() {
return "AccountDTO{" +
"id=" + id +
", userName='" + userName + '\'' +
", age=" + age +
", birthday=" + birthday +
", options=" + options +
", isDelete=" + isDelete +
", articles=" + articles +
'}';
}
}

View File

@ -0,0 +1,69 @@
/**
* Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
* <p>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mybatisflex.test;
import com.mybatisflex.core.MybatisFlexBootstrap;
import com.mybatisflex.core.audit.AuditManager;
import com.mybatisflex.core.audit.ConsoleMessageCollector;
import com.mybatisflex.core.audit.MessageCollector;
import com.mybatisflex.core.query.QueryWrapper;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import javax.sql.DataSource;
import java.util.List;
public class JoinTestStarter {
public static void main(String[] args) {
DataSource dataSource = new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("schema.sql")
.addScript("data.sql")
.build();
MybatisFlexBootstrap bootstrap = MybatisFlexBootstrap.getInstance()
.setDataSource(dataSource)
.addMapper(AccountMapper.class)
.addMapper(MyAccountMapper.class)
.start();
//开启审计功能
AuditManager.setAuditEnable(true);
//设置 SQL 审计收集器
MessageCollector collector = new ConsoleMessageCollector();
AuditManager.setMessageCollector(collector);
AccountMapper accountMapper = bootstrap.getMapper(AccountMapper.class);
// List<Account> accounts = accountMapper.selectListByQuery(QueryWrapper.create());
// System.out.println(accounts);
List<AccountDTO> accountDTOS = accountMapper.selectListByQueryAs(QueryWrapper.create(),AccountDTO.class);
System.out.println(accountDTOS);
//
// List<ArticleDTO> articleDTOS = accountMapper.selectListByQueryAs(QueryWrapper.create(), ArticleDTO.class);
// System.out.println(articleDTOS);
//
//
// Page<ArticleDTO01> paginate = accountMapper.paginateAs(Page.of(1, 10), QueryWrapper.create(), ArticleDTO01.class);
// System.out.println(paginate);
}
}

View File

@ -5,9 +5,9 @@ spring:
# enabled: true
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mp_test
url: jdbc:mysql://localhost:3306/flex_test
username: root
password: 12345678
password: 123456
# sql:
# init:
# schema-locations: classpath:schema.sql

View File

@ -1,19 +1,3 @@
/*
Navicat Premium Data Transfer
Source Server : MySQL3306
Source Server Type : MySQL
Source Server Version : 80024
Source Host : localhost:3306
Source Schema : mp_test
Target Server Type : MySQL
Target Server Version : 80024
File Encoding : 65001
Date: 08/06/2023 19:48:10
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
@ -24,12 +8,11 @@ DROP TABLE IF EXISTS `tb_good`;
CREATE TABLE `tb_good`
(
`good_id` int NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL,
`price` decimal(10, 2) NULL DEFAULT NULL,
PRIMARY KEY (`good_id`) USING BTREE
) ENGINE = InnoDB
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
ROW_FORMAT = Dynamic;
-- ----------------------------
@ -67,7 +50,6 @@ CREATE TABLE `tb_order`
PRIMARY KEY (`order_id`) USING BTREE
) ENGINE = InnoDB
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
ROW_FORMAT = Dynamic;
-- ----------------------------
@ -96,7 +78,6 @@ CREATE TABLE `tb_order_good`
`good_id` int NOT NULL
) ENGINE = InnoDB
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
ROW_FORMAT = Dynamic;
-- ----------------------------
@ -136,12 +117,11 @@ DROP TABLE IF EXISTS `tb_role`;
CREATE TABLE `tb_role`
(
`role_id` int NOT NULL AUTO_INCREMENT,
`role_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`role_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`role_key` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL,
`role_name` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL,
PRIMARY KEY (`role_id`) USING BTREE
) ENGINE = InnoDB
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
ROW_FORMAT = Dynamic;
-- ----------------------------
@ -163,12 +143,11 @@ DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user`
(
`user_id` int NOT NULL AUTO_INCREMENT,
`user_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`user_name` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL,
`password` varchar(255) CHARACTER SET utf8mb4 NULL DEFAULT NULL,
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
ROW_FORMAT = Dynamic;
-- ----------------------------
@ -193,7 +172,6 @@ CREATE TABLE `tb_user_order`
`order_id` int NOT NULL
) ENGINE = InnoDB
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
ROW_FORMAT = Dynamic;
-- ----------------------------
@ -222,7 +200,6 @@ CREATE TABLE `tb_user_role`
`role_id` int NOT NULL
) ENGINE = InnoDB
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_0900_ai_ci
ROW_FORMAT = Dynamic;
-- ----------------------------