Python 官方文档:入门教程 => 点击学习
目录一、什么业务情况会使用discriminator标签?二、discriminator使用Mapper接口配置测试三、discriminator原理Discriminator对象结
假设我们有一张user表:
使用查询语句select * from user
有如下需求
当用户年龄为18岁时查询结果显示生日信息。
当用户年龄不为18岁时查询结果不显示生日信息。(显示为空或NULL即可)
如果我们使用mybatis,第一个想到的解决办法可能是在Java程序里把用户信息查出来,然后再根据年龄做if
判断。但是这样做有点繁琐。还好mybatis提供了一个标签(<discriminator/>
)来解决如上的业务需求。discriminator
也就是侦察器、也叫鉴别器
还是结合第一节的业务问题来使用discriminator
标签
mapper.xml配置内容如下
<resultMap id="userMapForTestDiscriminator" type="user" autoMapping="false">
<!--关闭自动映射,那么没有指定的列名不会出现在结果集中-->
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="age" column="age"/>
<discriminator javaType="int" column="age">
<case value="18" resultType="user">
<result property="birthday" column="birthday"/>
</case>
</discriminator>
</resultMap>
<select id="selectDiscriminator" resultMap="userMapForTestDiscriminator">
select * from user limit 2
</select>
select标签的id为selectDiscriminator,并且返回结果集使用resultMap来接收。重点就在resultMap里配置了discriminator。先来解释一下discriminator的作用:
discriminator有个子标签是case,并且指定侦察器鉴别的属性为age。(它的用法很类似于Java中的switch)
当查询的结果集中age列数据等于case指定的value值时(在这个例子里就是当结果集中age列为18时)。则把case标签中的result标签加入到外部的resultMap标签中。反之——如果结果集中的age列值不为18,则不做任何操作。
换个更直观的说法来看。
<resultMap id="userMapForTestDiscriminator" type="user" autoMapping="false">
<!--关闭自动映射,那么没有指定的列名不会出现在结果集中-->
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="age" column="age"/>
<result property="birthday" column="birthday"/>
</resultMap>
<resultMap id="userMapForTestDiscriminator" type="user" autoMapping="false">
<!--关闭自动映射,那么没有指定的列名不会出现在结果集中-->
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="age" column="age"/>
</resultMap>
怎么样?是不是和Java中的Switch用法一模一样,当满足条件时就执行case语句中的代码。
public interface UserMapper {
List<User> selectDiscriminator();
}
@Test
public void test1() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.selectDiscriminator();
for (Object user : users) {
System.out.println(user);
}
}
输出结果:User(id=1, username=0.620544543088072, age=18, passWord=222, birthday=2002-10-27, author=null)
User(id=3, username=0.620544543088072, age=22, password=null, birthday=null, author=null)
从输出结果中已经可以看到,当age不等于18的时候,birthdary字段没有值。那么mybatis是如何实现这个功能的呢?下面来从源码角度分析下。
discriminator相当于Java中的Switch,作用是可以动态的控制resultMap
标签中的result
标签。
mybatis初始化的时候会加载映射配置文件。
XxxMapper.xml
文件解析为MappedStatement
对象保存在Configuration
对象中。以便于后续的调用。XxxMapper.xml
中的resultMap
标签都会被解析为ResultMap
对象存储在MappedStatement
对象当中discriminator
标签都会被解析为Discriminator
对象存储在ResultMap
对象当中。至此,Discriminator
所在的层级就是:
下面就从源码的两个方面揭开discriminator的面纱。
初始化时——加载配置文件并把discriminator标签解析为Discriminator
对象存储到内存中。
执行SQL时——如果检测到有与该SQL匹配的Discriminator
对象,则调用Discriminator
对象的逻辑
public class Discriminator {
private ResultMapping resultMapping;
private Map<String, String> discriminatORMap;
}
Discriminator对象有两个字段
<discriminator javaType="int" column="age">
中的age字段的相关信息(列名、jdbc类型、Java类型、是否嵌套等等。总之记录的是鉴别器鉴别的那个列的列信息。而这个信息被封装成了ResultMapping对象,比较简单感兴趣的可以看下ResultMapping对象源码)<case value="18" resultType="user">
case标签的信息。它是一个map结构,key是case中value属性的值,value是resultmap的的唯一标识。(mybatis会通过resultMap的唯一标识去configuration对象中寻找对应的resultMap)看到这里,默认读者有看过一定的mybatis源码。mybatis在启动时通过XMLMapperBuilder
来加载映射文件(xml文件)。其中包含了对ResultMap标签的解析过程。具体逻辑在XMLMapperBuilder#resultMapElement
方法中。代码如下(只列出了有关Discriminator的逻辑)
private ResultMap resultMapElement(Xnode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) {
// 1. 解析配置文件的标签创建为Discriminator对象
Discriminator discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
// 2. 构建ResultMapResolver对象,再调用resolve方法创建ResultMap对象,Discriminator就是在resolve方法中被ResultMap对象中的
// 说白了是构建ResultMap的辅助类。
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
return resultMapResolver.resolve();
}
调用processDiscriminatorElement
方法为Discriminator对象设置详细的属性。
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) {
// 通过 column javaType jdbcType typeHandler javaTypeClass typeHandlerClass
// 来构造Discriminator对象中的resultMapping字段
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String typeHandler = context.getStringAttribute("typeHandler");
Class<?> javaTypeClass = resolveClass(javaType);
Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
Map<String, String> discriminatorMap = new HashMap<>();
// 遍历discriminator标签,把case节点的信息封装为Discriminator对象中的discriminatorMap字段
for (XNode caseChild : context.getChildren()) {
String value = caseChild.getStringAttribute("value");
String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings, resultType));
discriminatorMap.put(value, resultMap);
}
// 调用辅助类整整的构建Discriminator对象
return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
}
至此,Discriminator对象就被完整的创建出来了。它会被添加在ResultMap对象中缓存。后续程序执行过程中,就能通过Configuration对象获取MappedStatement对象,再通过MappedStatement对象获取ResultMap对象,再通过ResultMap对象就可以获取Discriminator对象啦!
需要注意的是,每个case标签都会被解析为一个结果集间接存入到configuration对象中。
在程序通过mybatis执行数据库操作时,会通过ResultSetHandler
对象来处理数据库返回的结果集。ResultSetHandler
是mybatis的几个核心对象之一。它在DefaultResultSetHandler#resolveDiscriminatedResultMap
方法中进行对Discriminator的逻辑处理。方法代码如下
public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
// 1 通过resultMap获取鉴别器对象Discriminator
Discriminator discriminator = resultMap.getDiscriminator();
while (discriminator != null) {
// 2. 从鉴别器中获取结果集中对应的需要被鉴别的值。拿文章开头的业务举例,在此就是获取结果集中的age列的值,第一行age的值是18。也就是value的值为18。第二行age的值为22
final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
final String discriminatedMapid = discriminator.getMapIdFor(String.valueOf(value));
// 3. 判断configuration对象中是否有指定的resultMap对象,后面会根据这个resultMap进行映射结果集
if (configuration.hasResultMap(discriminatedMapId)) {
resultMap = configuration.getResultMap(discriminatedMapId);
}
}
return resultMap;
}
代码逻辑的大致步骤如下
ResultSetHandler
去处理映射关系。以上就是MyBatis discriminator标签原理实例解析的详细内容,更多关于MyBatis discriminator标签的资料请关注编程网其它相关文章!
--结束END--
本文标题: MyBatisdiscriminator标签原理实例解析
本文链接: https://lsjlt.com/news/194242.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-03-01
2024-03-01
2024-03-01
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0