如何将数据库表中的下划线的字段名称,映射成Java代码中的驼峰字段?
employee_name
表字段描述。employeeName
的方式进行表示。注意
:在 Mybatis 中也可以使用例如 employee_name as employeeName
的方式进行处理,但在整个编程中并不是太优雅。
as
映射,那么使用一个统一的字段映射更加合理。用一个标准的结构适配非映射类的对象属性。
select
语句下配置的 resultType
时,其实就已经添加了 ResultMap、ResultMapping
的映射结构。property、column
字段构建出 ResultMapping 结果映射类。
Map resultMaps
的结果映射中。mybatis-step-13
|-src
|-main
| |-java
| |-com.lino.mybatis
| |-annotations
| | |-Delete.java
| | |-Insert.java
| | |-Select.java
| | |-Update.java
| |-binding
| | |-MapperMethod.java
| | |-MapperProxy.java
| | |-MapperProxyFactory.java
| | |-MapperRegistry.java
| |-builder
| | |-annotations
| | | |-MapperAnnotationBuilder.java
| | |-xml
| | | |-XMLConfigBuilder.java
| | | |-XMLMapperBuilder.java
| | | |-XMLStatementBuilder.java
| | |-BaseBuilder.java
| | |-MapperBuilderAssistant.java
| | |-ParameterExpression.java
| | |-ResultMapResolver.java
| | |-SqlSourceBuilder.java
| | |-StaticSqlSource.java
| |-datasource
| | |-druid
| | | |-DruidDataSourceFacroty.java
| | |-pooled
| | | |-PooledConnection.java
| | | |-PooledDataSource.java
| | | |-PooledDataSourceFacroty.java
| | | |-PoolState.java
| | |-unpooled
| | | |-UnpooledDataSource.java
| | | |-UnpooledDataSourceFacroty.java
| | |-DataSourceFactory.java
| |-executor
| | |-parameter
| | | |-ParameterHandler.java
| | |-result
| | | |-DefaultResultContext.java
| | | |-DefaultResultHandler.java
| | |-resultset
| | | |-DefaultResultSetHandler.java
| | | |-ResultSetHandler.java
| | | |-ResultSetWrapper.java
| | |-statement
| | | |-BaseStatementHandler.java
| | | |-PreparedStatementHandler.java
| | | |-SimpleStatementHandler.java
| | | |-StatementHandler.java
| | |-BaseExecutor.java
| | |-Executor.java
| | |-SimpleExecutor.java
| |-io
| | |-Resources.java
| |-mapping
| | |-BoundSql.java
| | |-Environment.java
| | |-MappedStatement.java
| | |-ParameterMapping.java
| | |-ResultFlag.java
| | |-ResultMap.java
| | |-ResultMapping.java
| | |-SqlCommandType.java
| | |-SqlSource.java
| |-parsing
| | |-GenericTokenParser.java
| | |-TokenHandler.java
| |-reflection
| | |-factory
| | | |-DefaultObjectFactory.java
| | | |-ObjectFactory.java
| | |-invoker
| | | |-GetFieldInvoker.java
| | | |-Invoker.java
| | | |-MethodInvoker.java
| | | |-SetFieldInvoker.java
| | |-property
| | | |-PropertyNamer.java
| | | |-PropertyTokenizer.java
| | |-wrapper
| | | |-BaseWrapper.java
| | | |-BeanWrapper.java
| | | |-CollectionWrapper.java
| | | |-DefaultObjectWrapperFactory.java
| | | |-MapWrapper.java
| | | |-ObjectWrapper.java
| | | |-ObjectWrapperFactory.java
| | |-MetaClass.java
| | |-MetaObject.java
| | |-Reflector.java
| | |-SystemMetaObject.java
| |-scripting
| | |-defaults
| | | |-DefaultParameterHandler.java
| | | |-RawSqlSource.java
| | |-xmltags
| | | |-DynamicContext.java
| | | |-MixedSqlNode.java
| | | |-SqlNode.java
| | | |-StaticTextSqlNode.java
| | | |-XMLLanguageDriver.java
| | | |-XMLScriptBuilder.java
| | |-LanguageDriver.java
| | |-LanguageDriverRegistry.java
| |-session
| | |-defaults
| | | |-DefaultSqlSession.java
| | | |-DefaultSqlSessionFactory.java
| | |-Configuration.java
| | |-ResultContext.java
| | |-ResultHandler.java
| | |-RowBounds.java
| | |-SqlSession.java
| | |-SqlSessionFactory.java
| | |-SqlSessionFactoryBuilder.java
| | |-TransactionIsolationLevel.java
| |-transaction
| | |-jdbc
| | | |-JdbcTransaction.java
| | | |-JdbcTransactionFactory.java
| | |-Transaction.java
| | |-TransactionFactory.java
| |-type
| | |-BaseTypeHandler.java
| | |-DateTypeHandler.java
| | |-IntegerTypeHandler.java
| | |-JdbcType.java
| | |-LongTypeHandler.java
| | |-StringTypeHandler.java
| | |-TypeAliasRegistry.java
| | |-TypeHandler.java
| | |-TypeHandlerRegistry.java
|-test
|-java
| |-com.lino.mybatis.test
| |-dao
| | |-IActivityDao.java
| |-po
| | |-Activity.java
| |-ApiTest.java
|-resources
|-mapper
| |-Activity_Mapper.xml
|-mybatis-config-datasource.xml
resultMapElements
方法,解析 resultMap
映射参数。
javaTypeClass、typeHandlerInstance
,以及封装 XML 配置的基本字段映射信息。DefaultResultSetHandler#applyPropertyMappings
的处理过程。DateTypeHandler.java
package com.lino.mybatis.type;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
/**
* @description: 日期类型处理器
* @author: lingjian
* @createDate: 2022/11/11 14:01
*/
public class DateTypeHandler extends BaseTypeHandler<Date> {
@Override
protected void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
ps.setTimestamp(i, new Timestamp((parameter).getTime()));
}
@Override
protected Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
Timestamp sqlTimestamp = rs.getTimestamp(columnName);
if (sqlTimestamp != null) {
return new Date(sqlTimestamp.getTime());
}
return null;
}
}
TypeHandlerRegistry.java
package com.lino.mybatis.type;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
/**
* @description: 类型处理器注册机
*/
public final class TypeHandlerRegistry {
private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<>(16);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLER_MAP = new HashMap<>(16);
public TypeHandlerRegistry() {
register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler());
register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(Date.class, new DateTypeHandler());
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
register(javaType, null, typeHandler);
}
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (null != javaType) {
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.computeIfAbsent(javaType, k -> new HashMap<>(16));
map.put(jdbcType, handler);
}
ALL_TYPE_HANDLER_MAP.put(handler.getClass(), handler);
}
@SuppressWarnings("unchecked")
public TypeHandler<?> getTypeHandler(Class<?> type, JdbcType jdbcType) {
return getTypeHandler((Type) type, jdbcType);
}
public boolean hasTypeHandler(Class<?> javaType) {
return hasTypeHandler(javaType, null);
}
public boolean hasTypeHandler(Class<?> javaType, JdbcType jdbcType) {
return javaType != null && getTypeHandler((Type) javaType, jdbcType) != null;
}
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) {
Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = TYPE_HANDLER_MAP.get(type);
TypeHandler<?> handler = null;
if (jdbcHandlerMap != null) {
handler = jdbcHandlerMap.get(jdbcType);
if (handler == null) {
handler = jdbcHandlerMap.get(null);
}
}
// type driver generics here
return (TypeHandler<T>) handler;
}
public TypeHandler<?> getMappingTypeHandler(Class<? extends TypeHandler<?>> handlerType) {
return ALL_TYPE_HANDLER_MAP.get(handlerType);
}
}
ResultFlag.java
package com.lino.mybatis.mapping;
/**
* @description: 结果标志
*/
public enum ResultFlag {
/**
* 结果标志
*/
ID, CONSTRUCTOR
}
ResultMapping.java
package com.lino.mybatis.mapping;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.JdbcType;
import com.lino.mybatis.type.TypeHandler;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 结果映射Map
*/
public class ResultMapping {
private Configuration configuration;
private String property;
private String column;
private Class<?> javaType;
private TypeHandler<?> typeHandler;
private List<ResultFlag> flags;
public ResultMapping() {
}
public static class Builder {
private ResultMapping resultMapping = new ResultMapping();
public Builder(Configuration configuration, String property, String column, Class<?> javaType) {
resultMapping.configuration = configuration;
resultMapping.property = property;
resultMapping.column = column;
resultMapping.javaType = javaType;
resultMapping.flags = new ArrayList<>();
}
public Builder typeHandler(TypeHandler<?> typeHandler) {
resultMapping.typeHandler = typeHandler;
return this;
}
public Builder flags(List<ResultFlag> flags) {
resultMapping.flags = flags;
return this;
}
public ResultMapping build() {
resolveTypeHandler();
return resultMapping;
}
private void resolveTypeHandler() {
if (resultMapping.typeHandler == null && resultMapping.javaType != null) {
Configuration configuration = resultMapping.configuration;
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
resultMapping.typeHandler = typeHandlerRegistry.getTypeHandler(resultMapping.javaType, null);
}
}
}
public Configuration getConfiguration() {
return configuration;
}
public String getProperty() {
return property;
}
public String getColumn() {
return column;
}
public Class<?> getJavaType() {
return javaType;
}
public TypeHandler<?> getTypeHandler() {
return typeHandler;
}
public List<ResultFlag> getFlags() {
return flags;
}
}
Map resultMaps
。resultMaps Key
获取到对应的 ResultMap 进行使用。ResultMap.java
package com.lino.mybatis.mapping;
import com.lino.mybatis.session.Configuration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
* @description: 结果映射
*/
public class ResultMap {
private String id;
private Class<?> type;
private List<ResultMapping> resultMappings;
private Set<String> mappedColumns;
public ResultMap() {
}
public static class Builder {
private ResultMap resultMap = new ResultMap();
public Builder(Configuration configuration, String id, Class<?> type, List<ResultMapping> resultMappings) {
resultMap.id = id;
resultMap.type = type;
resultMap.resultMappings = resultMappings;
}
public ResultMap build() {
resultMap.mappedColumns = new HashSet<>();
// 添加 mappedColums 字段
for (ResultMapping resultMapping : resultMap.resultMappings) {
final String column = resultMapping.getColumn();
if (column != null) {
resultMap.mappedColumns.add(column.toUpperCase(Locale.ENGLISH));
}
}
return resultMap;
}
}
public String getId() {
return id;
}
public Class<?> getType() {
return type;
}
public List<ResultMapping> getResultMappings() {
return resultMappings;
}
public Set<String> getMappedColumns() {
return mappedColumns;
}
public List<ResultMapping> getPropertyResultMappings() {
return resultMappings;
}
}
mappedColumns
映射字段中。并返回 resultMap
对象。ResultMapResolver.java
package com.lino.mybatis.builder;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.mapping.ResultMapping;
import java.util.List;
/**
* @description: 结果映射解析器
*/
public class ResultMapResolver {
private final MapperBuilderAssistant assistant;
private String id;
private Class<?> type;
private List<ResultMapping> resultMappings;
public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class<?> type, List<ResultMapping> resultMappings) {
this.assistant = assistant;
this.id = id;
this.type = type;
this.resultMappings = resultMappings;
}
public ResultMap resolve() {
return assistant.addResultMap(this.id, this.type, this.resultMappings);
}
}
resultMap
映射为主。resultMap
的参数映射配置也是用于解决数据库表中的字段与 Java 代码中的对象字段不一致的情况。BaseBuilder.java
package com.lino.mybatis.builder;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.TypeAliasRegistry;
import com.lino.mybatis.type.TypeHandler;
import com.lino.mybatis.type.TypeHandlerRegistry;
/**
* @description: 构建器的基类,建造者模式
*/
public class BaseBuilder {
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
public Configuration getConfiguration() {
return configuration;
}
protected Class<?> resolveAlias(String alias) {
return typeAliasRegistry.resolveAlias(alias);
}
/**
* 根据别名解析 Class 类型别名注册/事务管理器别名
*
* @param alias 别名
* @return 对象类型
*/
protected Class<?> resolveClass(String alias) {
if (alias == null) {
return null;
}
try {
return resolveAlias(alias);
} catch (Exception e) {
throw new RuntimeException("Error resolving class. Cause: " + e, e);
}
}
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
if (typeHandlerType == null) {
return null;
}
return typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
}
}
MapperBuilderAssistant.java
package com.lino.mybatis.builder;
import com.lino.mybatis.mapping.*;
import com.lino.mybatis.reflection.MetaClass;
import com.lino.mybatis.scripting.LanguageDriver;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.type.TypeHandler;
import java.util.ArrayList;
import java.util.List;
/**
* @description: 映射构建器助手,建造者
*/
public class MapperBuilderAssistant extends BaseBuilder {
private String currentNamespace;
private String resource;
public MapperBuilderAssistant(Configuration configuration, String resource) {
super(configuration);
this.resource = resource;
}
public ResultMapping buildResultMapping(Class<?> resultType, String property, String column, List<ResultFlag> flags) {
Class<?> javaTypeClass = resolveResultJavaType(resultType, property, null);
TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, null);
ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass);
builder.typeHandler(typeHandlerInstance);
builder.flags(flags);
return builder.build();
}
private Class<?> resolveResultJavaType(Class<?> resultType, String property, Class<?> javaType) {
if (javaType == null && property != null) {
try {
MetaClass metaResultType = MetaClass.forClass(resultType);
javaType = metaResultType.getSetterType(property);
} catch (Exception ignore) {
}
}
if (javaType == null) {
javaType = Object.class;
}
return javaType;
}
public String getCurrentNamespace() {
return currentNamespace;
}
public void setCurrentNamespace(String currentNamespace) {
this.currentNamespace = currentNamespace;
}
public String applyCurrentNamespace(String base, boolean isReference) {
if (base == null) {
return null;
}
if (isReference) {
if (base.contains(".")) {
return base;
}
} else {
if (base.startsWith(currentNamespace + ".")) {
return base;
}
if (base.contains(".")) {
throw new RuntimeException("Dots are not allowed in element names, please remove it from " + base);
}
}
return currentNamespace + "." + base;
}
/**
* 添加映射器语句
*/
public MappedStatement addMappedStatement(String id, SqlSource sqlSource, SqlCommandType sqlCommandType,
Class<?> parameterType, String resultMap, Class<?> resultType,
LanguageDriver lang) {
// 给id加上namespace前缀:com.lino.mybatis.test.dao.IUserDao.queryUserInfoById
id = applyCurrentNamespace(id, false);
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlCommandType, sqlSource, resultType);
// 结果映射, 给 MappedStatement#resultMaps
setStatementResultMap(resultMap, resultType, statementBuilder);
MappedStatement statement = statementBuilder.build();
// 映射语句信息,建造完存放到配置项中
configuration.addMappedStatement(statement);
return statement;
}
private void setStatementResultMap(String resultMap, Class<?> resultType, MappedStatement.Builder statementBuilder) {
// 因为暂时还没有在 Mapper XML 中配置 Map 返回结果,所以这里返回的是 null
resultMap = applyCurrentNamespace(resultMap, true);
List<ResultMap> resultMaps = new ArrayList<>();
if (resultMap != null) {
String[] resultMapNames = resultMap.split(",");
for (String resultMapName : resultMapNames) {
resultMaps.add(configuration.getResultMap(resultMapName.trim()));
}
}
/*
* 通常使用 resultType 即可满足大部分场景
*
else if (resultType != null) {
ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(
configuration, statementBuilder.id() + "-Inline", resultType, new ArrayList<>());
resultMaps.add(inlineResultMapBuilder.build());
}
statementBuilder.resultMaps(resultMaps);
}
public ResultMap addResultMap(String id, Class<?> type, List<ResultMapping> resultMappings) {
// 补全ID全路径,如:com.lino.mybatis.test.dao.IActivityDao + activityMap
id = applyCurrentNamespace(id, false);
ResultMap.Builder inlineResultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings);
ResultMap resultMap = inlineResultMapBuilder.build();
configuration.addResultMap(resultMap);
return resultMap;
}
}
buildResultMapping
。
buildResultMappingFromContext
所调用的 XMLMapperBuilder#buildResultMapping
方法。
的 column、property
字段。addResultMap
。
configurationElement
方法的处理内容。resultMap
操作。这部分操作也就是在解析整个 select、insert、update、delete
部分。XMLMapperBuilder.java
package com.lino.mybatis.builder.xml;
import com.lino.mybatis.builder.BaseBuilder;
import com.lino.mybatis.builder.MapperBuilderAssistant;
import com.lino.mybatis.builder.ResultMapResolver;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.mapping.ResultFlag;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.mapping.ResultMapping;
import com.lino.mybatis.session.Configuration;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @description: XML映射构建器
*/
public class XMLMapperBuilder extends BaseBuilder {
private Element element;
private String resource;
/**
* 映射器构建助手
*/
private MapperBuilderAssistant builderAssistant;
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource) throws DocumentException {
this(new SAXReader().read(inputStream), configuration, resource);
}
public XMLMapperBuilder(Document document, Configuration configuration, String resource) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.element = document.getRootElement();
this.resource = resource;
}
/**
* 解析
*
* @throws Exception 异常
*/
public void parse() throws Exception {
// 如果当前资源没有加载过再加载,防止重复加载
if (!configuration.isResourceLoaded(resource)) {
configurationElement(element);
// 标记一下,已经加载过了
configuration.addLoadedResource(resource);
// 绑定映射器到namespace
configuration.addMapper(Resources.classForName(builderAssistant.getCurrentNamespace()));
}
}
/**
* 配置mapper元素
*
*
*
*
* @param element 元素
*/
private void configurationElement(Element element) {
// 1.配置namespace
String namespace = element.attributeValue("namespace");
if ("".equals(namespace)) {
throw new RuntimeException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
// 2.解析resultMap
resultMapElement(element.elements("resultMap"));
// 3.配置select|insert|update|delete
buildStatementFromContext(element.elements("select"), element.elements("insert"), element.elements("update"), element.elements("delete"));
}
/**
* 解析resultMap
*
* @param list 结果映射列表
*/
private void resultMapElement(List<Element> list) {
for (Element element : list) {
try {
resultMapElement(element, Collections.emptyList());
} catch (Exception ignore) {
}
}
}
/**
*
*
*
*
*
*
*
*
*/
private ResultMap resultMapElement(Element resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
String id = resultMapNode.attributeValue("id");
String type = resultMapNode.attributeValue("type");
Class<?> typeClass = resolveClass(type);
List<ResultMapping> resultMappings = new ArrayList<>();
resultMappings.addAll(additionalResultMappings);
List<Element> resultChildren = resultMapNode.elements();
for (Element resultChild : resultChildren) {
List<ResultFlag> flags = new ArrayList<>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
// 构建 ResultMapping
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
// 创建结果映射解析器
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, resultMappings);
return resultMapResolver.resolve();
}
/**
*
*
*/
private ResultMapping buildResultMappingFromContext(Element context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
String property = context.attributeValue("property");
String column = context.attributeValue("column");
return builderAssistant.buildResultMapping(resultType, property, column, flags);
}
/**
* 配置select|insert|update|delete
*
* @param lists 元素列表
*/
@SafeVarargs
private final void buildStatementFromContext(List<Element>... lists) {
for (List<Element> list : lists) {
for (Element element : list) {
final XMLStatementBuilder statementBuilder = new XMLStatementBuilder(configuration, builderAssistant, element);
statementBuilder.parseStatementNode();
}
}
}
}
XMLMapperBuilder#configurationElement
配置元素解析的方法中,新增加了关于 resultMap
元素的解析。
elements
集合元素。resultMap
标签中,如
的 id、type
信息。
中的 column、property
信息。id
的配置专门用 ResultFlag 枚举类进行标记。DefaultResultSetHandler#handlerResultSets
结果收集器的操作中。DefaultResultSetHandler.java
package com.lino.mybatis.executor.resultset;
import com.lino.mybatis.executor.Executor;
import com.lino.mybatis.executor.result.DefaultResultContext;
import com.lino.mybatis.executor.result.DefaultResultHandler;
import com.lino.mybatis.mapping.BoundSql;
import com.lino.mybatis.mapping.MappedStatement;
import com.lino.mybatis.mapping.ResultMap;
import com.lino.mybatis.mapping.ResultMapping;
import com.lino.mybatis.reflection.MetaClass;
import com.lino.mybatis.reflection.MetaObject;
import com.lino.mybatis.reflection.factory.ObjectFactory;
import com.lino.mybatis.session.Configuration;
import com.lino.mybatis.session.ResultHandler;
import com.lino.mybatis.session.RowBounds;
import com.lino.mybatis.type.TypeHandler;
import com.lino.mybatis.type.TypeHandlerRegistry;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* @description: 默认Map结果处理器
* @author: lingjian
* @createDate: 2022/11/8 13:59
*/
public class DefaultResultSetHandler implements ResultSetHandler {
private static final Object NO_VALUE = new Object();
...
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
// 根据返回类型,实例化对象
Object resultObject = createResultObject(rsw, resultMap, null);
if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(resultObject);
// 自动映射:把每列的值都赋到对应的字段上
applyAutomaticMappings(rsw, resultMap, metaObject, null);
// Map映射:根据映射类型赋值到字段
applyPropertyMappings(rsw, resultMap, metaObject, null);
}
return resultObject;
}
...
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
for (ResultMapping propertyMapping : propertyMappings) {
final String column = propertyMapping.getColumn();
if (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH))) {
// 获取值
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
Object value = typeHandler.getResult(rsw.getResultSet(), column);
// 设置值
final String property = propertyMapping.getProperty();
if (value != NO_VALUE && property != null && value != null) {
// 通过反射工具类设置属性值
metaObject.setValue(property, value);
foundValues = true;
}
}
}
return foundValues;
}
}
DefaultResultSetHandler#handlerResultSets
方法开始,调用 handlerResultSet
方法,创建结果处理器、封装数据和保存结果。DefaultResultSetHandler#getRowValue
方法中,原有的是通过自动映射,把每列的值赋值到对应的字段上。applyPropertyMappings
方法进行处理。
applyPropertyMappings
首先获取 mappedColumnNames
映射的字段。List
时,进行比对判断是否包含当前字段。TypeHandler#getResult
获取结果。CREATE TABLE `activity` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID',
`activity_id` bigint(20) NOT NULL COMMENT '活动ID',
`activity_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '活动名称',
`activity_desc` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '活动描述',
`create_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `unique_activity_id`(`activity_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '活动配置' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of activity
-- ----------------------------
INSERT INTO `activity` VALUES (1, 100001, '活动名', '测试活动', '2021-08-08 20:14:50', '2021-08-08 20:14:50');
INSERT INTO `activity` VALUES (3, 100002, '活动名', '测试活动', '2021-10-05 15:49:21', '2021-10-05 15:49:21');
Activity.java
package com.lino.mybatis.test.po;
import java.util.Date;
/**
* @description: 活动类
*/
public class Activity {
/**
* 主键ID
*/
private Long id;
/**
* 活动ID
*/
private Long activityId;
/**
* 活动名称
*/
private String activityName;
/**
* 活动描述
*/
private String activityDesc;
/**
* 创建人
*/
private String creator;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新时间
*/
private Date updateTime;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getActivityId() {
return activityId;
}
public void setActivityId(Long activityId) {
this.activityId = activityId;
}
public String getActivityName() {
return activityName;
}
public void setActivityName(String activityName) {
this.activityName = activityName;
}
public String getActivityDesc() {
return activityDesc;
}
public void setActivityDesc(String activityDesc) {
this.activityDesc = activityDesc;
}
public String getCreator() {
return creator;
}
public void setCreator(String creator) {
this.creator = creator;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
}
IActivityDao.java
package com.lino.mybatis.test.dao;
import com.lino.mybatis.test.po.Activity;
/**
* @description: 活动持久层
*/
public interface IActivityDao {
/**
* 根据活动ID查询活动
*
* @param activityId 活动ID
* @return 活动对象
*/
Activity queryActivityById(Long activityId);
}
mybatis-config-datasource.xml
DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
dataSource>
environment>
environments>
<mappers>
<mapper resource="mapper/Activity_Mapper.xml"/>
mappers>
configuration>
Activity_Mapper.xml
DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lino.mybatis.test.dao.IActivityDao">
<resultMap id="activityMap" type="com.lino.mybatis.test.po.Activity">
<id column="id" property="id"/>
<result column="activity_id" property="activityId"/>
<result column="activity_name" property="activityName"/>
<result column="activity_desc" property="activityDesc"/>
<result column="create_time" property="createTime"/>
<result column="update_time" property="updateTime"/>
resultMap>
<select id="queryActivityById" parameterType="java.lang.Long" resultMap="activityMap">
SELECT activity_id, activity_name, activity_desc, create_time, update_time
FROM activity
WHERE activity_id = #{activityId}
select>
mapper>
ApiTest.java
package com.lino.mybatis.test;
import com.alibaba.fastjson.JSON;
import com.lino.mybatis.io.Resources;
import com.lino.mybatis.session.SqlSession;
import com.lino.mybatis.session.SqlSessionFactory;
import com.lino.mybatis.session.SqlSessionFactoryBuilder;
import com.lino.mybatis.test.dao.IActivityDao;
import com.lino.mybatis.test.po.Activity;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
/**
* @description: 单元测试
*/
public class ApiTest {
private Logger logger = LoggerFactory.getLogger(ApiTest.class);
private SqlSession sqlSession;
@Before
public void init() throws IOException {
// 1.从SqlSessionFactory中获取SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
sqlSession = sqlSessionFactory.openSession();
}
@Test
public void test_queryActivityById() {
// 1.获取映射器对象
IActivityDao dao = sqlSession.getMapper(IActivityDao.class);
// 2.测试验证
Activity result = dao.queryActivityById(100001L);
logger.info("测试结果:{}", JSON.toJSONString(result));
}
}
测试结果
16:37:14.750 [main] INFO c.l.mybatis.builder.SqlSourceBuilder - 构建参数映射 property:activityId propertyType:class java.lang.Long
16:37:14.794 [main] INFO c.l.m.s.defaults.DefaultSqlSession - 执行查询 statement:com.lino.mybatis.test.dao.IActivityDao.queryActivityById parameter:100001
16:37:15.518 [main] INFO c.l.m.d.pooled.PooledDataSource - Created connention 1516500233.
16:37:15.526 [main] INFO c.l.m.s.d.DefaultParameterHandler - 根据每个ParameterMapping中的TypeHandler设置对应的参数信息 value:100001
16:37:15.553 [main] INFO com.lino.mybatis.test.ApiTest - 测试结果:{"activityDesc":"测试活动","activityId":100001,"activityName":"活动名","createTime":1628424890000,"updateTime":1628424890000}