Mybatis中的resultMap和resultType 和parameterMap与 parameterType的区别
说实话Mybatis的知识点不少,在之前讲过mybatis的一级缓存与二级缓存(作用于在Executor组件)。而今天的主题是parameterMap 、parameterType、resultType、parameterMap,它们作用于ParameterHandler组件与ResultSetHandler 组件层。如果有人问你这类问题,其实是在考你的基本功(是否知道mybatis的架构或源码),这样才能get到对方关注的点。
在XML 映射文件中随处可用:parameterMap 、parameterType、resultType、parameterMap
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
select
<include refid="Base_Column_List" />
from bas_branchroute
where branch_route_id = #{branchRouteId,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String" >
delete from bas_branchroute
where branch_route_id = #{branchRouteId,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="com.xxx.oms.model.BasBranchrouteExample" >
delete from bas_branchroute
<if test="_parameter != null" >
<include refid="Example_Where_Clause" />
</if>
</delete>
基本用法
parameterMap
这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
parameterType
将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。
resultType
从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
resultMap
外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。
数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,而 ResultMap 就是 MyBatis 对这个问题的答案。它支持的元素有constructor(实例化类时注入)、association(关联一个对象)、collection(关联多个对象),可以表示对象间一对一、一对多等关系。更多介绍见官方文档
<!-- 构造方法 -->
<resultMap id="userMap" type="User">
<constructor>
<idArg column="id" javaType="int"/>
<arg column="username" javaType="String"/>
<arg column="age" javaType="_int"/>
</constructor>
</resultMap>
<!-- 一对一 -->
<resultMap id="userMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="address" column="address"></result>
<result property="email" column="email"></result>
<association property="role" javaType="Role">
<id property="id" column="role_id"></id>
<result property="name" column="role_name"></result>
</association>
</resultMap>
<!-- 一对多 -->
<resultMap id="userMap" type="User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="password" column="password"></result>
<result property="address" column="address"></result>
<result property="email" column="email"></result>
<collection property="roles" ofType="Role">
<id property="id" column="role_id"></id>
<result property="name" column="role_name"></result>
</collection>
</resultMap>
<!-- 一对多:嵌套 Select 查询 -->
<resultMap id="menusMap" type="Menu">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="url" column="url"></result>
<result property="m_desc" column="m_desc"></result>
<result property="parent_id" column="parent_id"></result>
<!-- ofType="Menu" 对应返回数据的类型-->
<!--select="getMenus" 指定了SELECT语句的id-->
<!--column="{parent_id=id}" 则是列的别名,参数的表达式-->
<collection property="childMenu" ofType="Menu" select="getMenus" column="{parent_id=id}"></collection>
</resultMap>
系统架构
关键组件
- SqlSession:selectOne、selectList、selectMap、select、insert、update、delete、commit、rollback、flushStatements、close、clearCache、getConfiguration、getMapper、getConnection
- Executor:update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
- StatementHandler:prepare, parameterize, batch, update, query
- ParameterHandler:getParameterObject, setParameters
- ResultSetHandler:handleResultSets, handleOutputParameters
内部交互
StatementHandler是非常重要的一层,它主要是对Statement的各种特殊处理(不负责执行),它支持三种Statement、
Prepared、Callable模式,默认值:Prepared。RoutingStatementHandler提供了一种适配模式化的统一入口。
源码解析
你理解了上面的内容,再回头看源码就不复杂了!源码分析基于mybatis-3.4.4版本展开。
ParameterHandler
public interface ParameterHandler {
//获取参数对象
Object getParameterObject();
//设置参数对象
void setParameters(PreparedStatement ps) throws SQLException;
}
public class DefaultParameterHandler implements ParameterHandler {
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
if (parameterMappings != null) {
for(int i = 0; i < parameterMappings.size(); ++i) {//遍历参数
ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
String propertyName = parameterMapping.getProperty();//参数名称
Object value;//参数值
if (this.boundSql.hasAdditionalParameter(propertyName)) {//是否有这个参数
value = this.boundSql.getAdditionalParameter(propertyName);//获取参数值
} else if (this.parameterObject == null) {
value = null;
} else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
value = this.parameterObject;
} else {
MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);//这个是什么鬼,不急慢慢看
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = this.configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException var10) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
} catch (SQLException var11) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var11, var11);
}
}
}
}
}
}
问题1:确实setParameters()是关键点,它是什么时候被触发的呢?
它分别为 PreparedStatementHandler 和 CallableStatementHandler (执行存储过程)的 parameterize()中被调用。它在构建Statement 时被触发。
public class SimpleExecutor extends BaseExecutor {
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
//构建Statement对象
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
//此时很关键,最终路径:RoutingStatementHandler.parameterize -> PreparedStatementHandler.parameterize
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
}
public class PreparedStatementHandler extends BaseStatementHandler {
public void parameterize(Statement statement) throws SQLException {
//最终执行这个
parameterHandler.setParameters((PreparedStatement) statement);
}
}
问题2:MetaObject是什么鬼?
MetaObject是将Properties映射为bean属性,实际上就是提供 类|集合|Map
的一种自动识别的访问形式。PropertyTokenizer是的属性分词器,提供了对象和集合的一种概念形式,如: object.parent.name(
只是记录了 object 而已,可以通过next方法创建一个对象,返回new PropertyTokenizer(“parent”) 再次调用next 返回new PropertyTokenizer(“name”))。
//基本用法
@Test
public void testGetSet(){
Animal animal = new Animal();
//testBean
MetaObject metaObject = MetaObject.forObject(animal, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
metaObject.setValue("name","bean");
System.out.println(animal.getName());
//testMap
animal.setName("map");
Map<String,Animal> map = new HashMap<>();
metaObject = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
metaObject.setValue("deer",animal);
System.out.println(map.get("deer").getName());
//testCollection
animal.setName("collection");
List<Animal> list = new ArrayList<>();
metaObject = MetaObject.forObject(list, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
metaObject.add(animal);
System.out.println(list.get(0).getName());
//testTokenizer
animal.setParent(new Animal());
metaObject = MetaObject.forObject(animal, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
metaObject.setValue("parent.name","tokenizer");
System.out.println(animal.getParent().getName());
}
//源码
public class MetaObject {
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
this.originalObject = object;//原始对象
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
if (object instanceof ObjectWrapper) {//原始对象为ObjectWrapper
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {//原始对象为Map,提供了 object.setName,object.setAge 以及类似 object.getParent().setName(),object.getParent().getParent().setName() 的赋值方式
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {//原始对象为Collection,提供了集合的add,addAll 方法
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {//默认为BeanWrapper,提供的赋值与map相同
this.objectWrapper = new BeanWrapper(this, object);
}
}
//构建
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
if (object == null) {
return NULL_META_OBJECT;//记录Class相关的getter
} else {
return new MetaObject(object, objectFactory, objectWrapperFactory);
}
}
public Object getValue(String name) {
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == MetaObject.NULL_META_OBJECT) {
return null;
} else {
return metaValue.getValue(prop.getChildren());
}
} else {
return objectWrapper.get(prop);
}
}
public void setValue(String name, Object value) {
//属性分词器,识别属性.符号
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) {
MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
if (metaValue == MetaObject.NULL_META_OBJECT) {
if (value == null && prop.getChildren() != null) {
return; // don't instantiate child path if value is null
} else {
metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
}
}
metaValue.setValue(prop.getChildren(), value);
} else {
objectWrapper.set(prop, value);
}
}
}
ResultSetHandler
还是按老的套路,以handleResultSets()为突破口,它主要是对Statement执行后产生的结果集转化成List。
public interface ResultSetHandler {
//将Statement执行后产生的结果集转化成list
<E> List<E> handleResultSets(Statement var1) throws SQLException;
//将Statement执行后产生的结果集转化成cursor
<E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
//处理存储过程执行后的输出参数
void handleOutputParameters(CallableStatement var1) throws SQLException;
}
public class DefaultResultSetHandler implements ResultSetHandler {
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);//结果集的第一个结果
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
//根据resultMap处理rsw生成java对象,最关键的一步,最终调用getRowValue
handleResultSet(rsw, resultMap, multipleResults, null);
//获取结果集的下一个结果
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResultSets();
if (resultSets != null) {//和resultMaps的遍历处理类似
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
return collapseSingleResultList(multipleResults);
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
try {
if (parentMapping != null) {
handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
} else {
if (resultHandler == null) {
DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
//最终会调用getRowValue
handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
multipleResults.add(defaultResultHandler.getResultList());
} else {//最终会调用getRowValue
handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
}
}
} finally {
// issue #228 (close resultsets)
closeResultSet(rsw.getResultSet());
}
}
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//生成对象
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {//自动映射
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
}
//property映射,ResultLoaderMap属性嵌套查询且懒加载
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
}
return rowValue;
}
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {//存在适用的typeHanlder类,事实上一般为基本数据类型或者其封装类
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {//有参构造函数的constructor映射
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {//接口或者无参构造函数
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {//有参构造函数的自动映射
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
}
它的被调用链不复杂,基本上可以直接定位到SimpleStatementHandler。
此外还有一点细节:在xml中select的查询中配置的不是resultMap而是resultType,在mybatis中也是按照resultMap进行存储的,具体验证可看下面源码分析。
public class SimpleStatementHandler extends BaseStatementHandler {
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.<E>handleResultSets(statement);//直接调用我们关注的方法
}
}
public class MapperBuilderAssistant extends BaseBuilder {
//在addMappedStatement方法(构建MappedStatement,它非常关键)中被调用
private List<ResultMap> getStatementResultMaps(
String resultMap,
Class<?> resultType,
String statementId) {
resultMap = applyCurrentNamespace(resultMap, true);
List<ResultMap> resultMaps = new ArrayList<ResultMap>();
if (resultMap != null) {//resultMap配置支持多个用,分割的resultMap
String[] resultMapNames = resultMap.split(",");
for (String resultMapName : resultMapNames) {
try {
resultMaps.add(configuration.getResultMap(resultMapName.trim()));
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("Could not find result map " + resultMapName, e);
}
}
} else if (resultType != null) {//resultType会构建一个inlineResultMap
ResultMap inlineResultMap = new ResultMap.Builder(
configuration,
statementId + "-Inline",
resultType,
new ArrayList<ResultMapping>(),
null).build();
resultMaps.add(inlineResultMap);
}
return resultMaps;
}
}
/**
* StatementHandler中通过它可以获取关于Statement所有细节
**/
public final class MappedStatement {
private String resource;
private Configuration configuration;//配置
private String id;//mapper中的id
private Integer fetchSize;//batch需要
private Integer timeout;//超时
private StatementType statementType;
private ResultSetType resultSetType;
private SqlSource sqlSource;//数据源
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
public BoundSql getBoundSql(Object parameterObject) {//可执行的sql
...
}
}
看源码其实没有什么技巧,要大胆提问+你的猜想,然后去验证,并从中体会学习优秀的设计。
还没有评论,来说两句吧...