这一章节主要解决好几章之前留下的坑,需要根据XML配置的ReultMap进行解析映射成具体的PO供用户使用。
我们本章就来解决下在xml中配置了如下标红框的内容,怎么解析映射到具体的实体类中,如下就是将id为activityMap的resultMap怎么解析到type为Activity实体中,并将一个一个的redult的字段属性获取到以后映射到Activity的属性里,并在最终查询获取时用到了这个resultMap的结果进行属性值的反射就可以了,
下面这张图是xml与各属性之间对应的关系,我们需要根据这个关系的逻辑处理对应的映射。
首先需要有xml的resultMap标签,去定义标识id以及对应的PO实体type,然后对应的类resultMap去存储id以及type等信息,然后字段的就定义在result的column和property,主要一个是数据库字段属性,一个是实体里的属性,然后到时解析到resultMapping对应属性里,
解析完了以后执行sql语句时返回结果用的是resultMap,并把对应的resultMap类数据取出获得ResultMapping与数据库数据一一比对,匹配则进入反射并设置对应的数据库值即可。
代码还是蛮简单的,涉及的东西也不多,主要围绕着解析然后映射字段等处理来。
1.主要在XmlMapperBuilder里添加解析resultMap的方法,如resultMapElement方法,解析获取resultMap的id和type,并通过buildResultMappingFromContext方法解析resultMapping的属性和列,解析完毕构建存储到resultMapping,并最终存储到resultMap中,数据都处理好了,
2.resultMap数据都处理好了后,传递到ResultMapResolver里,最终由此调用MapperBuilderAssistant助手进行resultMap的构建。
3.Sql执行完处理结果时,添加新的映射结果集处理。
XMLMapperBuilder类修改如下:
在configurationElement方法里新增解析resultMap操作,新增resultMapElement方法用来解析resultMap,新增buildResultMappingFromContext方法来解析resultMapping,其实都不复杂就这点业务逻辑,打个断点可以看下
public class XMLMapperBuilder extends BaseBuilder {
// 省略其他
private void configurationElement(Element element) {
// 配置namespace ,代码省略中
// 2. 解析resultMap step-13 新增
resultMapElements(element.elements("resultMap"));
// select、insert、update、delete SQL解析
}
private void resultMapElements(List list) {
for (Element element : list) {
try {
resultMapElement(element, Collections.emptyList());
} catch (Exception ignore) {
}
}
}
/**
* 解析resultMap
*
*
*
*
*
*
*
*
*/
private void resultMapElement(Element resultMapNode, List additionalResultMappings) {
String id = resultMapNode.attributeValue("id"); // resultMap id
String type = resultMapNode.attributeValue("type"); // resultMap type
// 将resultMap的type转换Class
Class> typeClass = resolveClass(type);
List resultMappings = new ArrayList<>();
resultMappings.addAll(additionalResultMappings);
// resultMap所有的子result
List resultChildren = resultMapNode.elements();
for (Element resultChild : resultChildren) {
List 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);
resultMapResolver.resolve();
}
/**
*
*
*/
private ResultMapping buildResultMappingFromContext(Element context, Class> resultType, List flags) {
String property = context.attributeValue("property"); // 需要被映射的属性,如:activityId
String column = context.attributeValue("column"); // 原列,如activity_id
return builderAssistant.buildResultMapping(resultType, property, column, flags);
}
}
MapperBuilderAssistant主要添加了构建resultMapping方法操作,以及获取javaType下属性的set方法的参数类型方法处理。
public class MapperBuilderAssistant extends BaseBuilder {
// step-13 新增方法
public ResultMapping buildResultMapping(
Class> resultType,
String property,
String column,
List flags) {
// 获取resultType的里属性当前SET方法的参数类型
Class> javaTypeClass = resolveResultJavaType(resultType, property, null);// 列的结果类型
TypeHandler> typeHandlerInstance = resolveTypeHandler(javaTypeClass, null);
// 构建ResultMapping
ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass);
builder.typeHandler(typeHandlerInstance);
builder.flags(flags);
return builder.build();
}
// new add
// 根据resultType获取javaType
private Class> resolveResultJavaType(Class> resultType, String property, Class> javaType) {
if (javaType == null && property != null) {
try {
MetaClass metaResultType = MetaClass.forClass(resultType);
// 根据当前属性获取set参数类型
javaType = metaResultType.getSetterType(property);
} catch (Exception ignore) {
}
}
if (javaType == null) {
javaType = Object.class;
}
return javaType;
}
}
BaseBuilder主要说新增两个从注册器获取类型操作,一个是resolveClass方法里从typeAliasRegistry获取,获取不到的化直接将当前类型反射成类(通过Resources.classForName(string)),
另一个是resolveTypeHandler方法里从typeHandlerRegistry里获取java类型,前提是typeHandlerType不为空,否则返回空,但是我们上边MapperBuilderAssistant类buildResultMapping()里传的是空,所以这里一点返回空,没关系后边会填这个坑
public class BaseBuilder {
// 省略其他...
// 根据类型名称反射成Class 类型
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);
}
}
// new add 获取TypeHandler
protected TypeHandler> resolveTypeHandler(Class> javaType, Class extends TypeHandler>> typeHandlerType) {
if (typeHandlerType == null) {
return null;
}
return typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
}
}
ResultMapResolver新增的类,在这里主要的方法就是调用助手类构建resultMap,特意封装了一个ResultMapResolver类。
ublic class ResultMapResolver {
private final MapperBuilderAssistant assistant;
private String id;
private Class> type;
private List resultMappings;
public ResultMapResolver(MapperBuilderAssistant assistant, String id, Class> type, List resultMappings) {
this.assistant = assistant;
this.id = id;
this.type = type;
this.resultMappings = resultMappings;
}
public ResultMap resolve() {
// 通过助手将resultMapping放入resultMap里
return assistant.addResultMap(this.id, this.type, this.resultMappings);
}
}
ResultMapping类的修改,新增了静态内部类帮忙构造ResultMapping所需要的属性,其中 resolveTypeHandler()方法就是解决上面说的typeHandler为空的问题,
public class ResultMapping {
private Configuration configuration;
private String property;
private String column;
private Class> javaType;
private TypeHandler> typeHandler;
private List flags;
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 flags) {
resultMapping.flags = flags;
return this;
}
public ResultMapping build() {
// 构建ResultMapping时TypeHandler为空,再次获取TypeHandler
resolveTypeHandler();
return resultMapping;
}
private void resolveTypeHandler() {
if (resultMapping.typeHandler == null && resultMapping.javaType != null) {// javaType不为空,因为根据此来获取类型处理器
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 void setProperty(String property) {
this.property = property;
}
public String getColumn() {
return column;
}
public Class> getJavaType() {
return javaType;
}
public TypeHandler> getTypeHandler() {
return typeHandler;
}
public List getFlags() {
return flags;
}
}
resultMap就只是更改了在build方法时将resultMapping.getColumn()转为大写存入mappedColumns中。
public class ResultMap {
private String id;
private Class> type;
private List resultMappings;
private Set mappedColumns;
public ResultMap() {
}
public static class Builder {
private ResultMap resultMap = new ResultMap();
public Builder(Configuration configuration, String id, Class> type, List resultMappings) {
resultMap.id = id;
resultMap.type = type;
resultMap.resultMappings = resultMappings;
}
public ResultMap build() {
resultMap.mappedColumns = new HashSet<>();
// new add
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 getResultMappings() {
return resultMappings;
}
public Set getMappedColumns() {
return mappedColumns;
}
public List getPropertyResultMappings() {
return resultMappings;
}
}
Configuration类中添加了resultMap的存储与获取
// 结果映射,存在Map里-新增
protected final Map resultMaps = new HashMap<>();
public ResultMap getResultMap(String id) {
return resultMaps.get(id);
}
public void addResultMap(ResultMap resultMap) {
resultMaps.put(resultMap.getId(), resultMap);
}
前面是解析,到DefaultResultSetHandler就是反射和使用了,在getRowValue()方法里添加新的方法applyPropertyMappings()迎来处理resultMap映射,然后跟上节其他的自动映射的操作一样获取java类型,根据类型从结果集获取结果值,然后通过反射工具往对象里设置值。
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 mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
boolean foundValues = false;
// 获取ResultMapping数据
final List 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;
}
添加新的xml,名字为:Activity_Mapper.xml
mybatis-config-datasource.xml,配置成Activity_Mapper.xml
单元测试
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 res = dao.queryActivityById(100001L);
System.out.println(res.getActivityName());
//logger.info("测试结果:{}", JSON.toJSONString(res));
}
建表Sql语句
DROP TABLE IF EXISTS `activity`;
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(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime(0) 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');
单元测试完毕,拿到了想要的值