前言:
在mybatis中,映射文件中的namespace是用于绑定Dao接口的,即面向接口编程。
当你的namespace绑定接口后,你可以不用写接口实现类,mybatis会通过该绑定自动帮你找到对应要执行的SQL语句。
但是,在实际编程过程中,也可以使用实体类的class名称作为namespace进行匹配。
正文:
话不多,先上代码:
SqlSession session = this.sqlSessionFactory.openSession();
Map paramMap = new HashMap(16);
try {
ZoneId zoneId = ZoneId.systemDefault();
paramMap.put("startTime", Date.from(startTime.atZone(zoneId).toInstant()));
paramMap.put("endTime", Date.from(endTime.atZone(zoneId).toInstant()));
Map aMap= session.selectOne("selectAMap", paramMap);
return aMap;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (null != session) {
session.close();
}
}
编写完这段代码后,就想起之前想过研究下这里是selectOne里面的statement参数中指定的这个id是如何跟对应Mapper.xml中的id匹配起来的,这里猜测可能是根据namespace进行匹配,遂跟进代码。
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
List var6;
try {
MappedStatement ms = this.configuration.getMappedStatement(statement);
List<E> result = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
var6 = result;
} catch (Exception var10) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var10, var10);
} finally {
ErrorContext.instance().reset();
}
return var6;
}
这段代码中,发现MappedStatement类,emmm…应该在在这里做的处理,点进去看。
public MappedStatement getMappedStatement(String id, boolean validateIncompleteStatements) {
if (validateIncompleteStatements) {
this.buildAllStatements();
}
return (MappedStatement)this.mappedStatements.get(id);
}
buildAllStatements()此方法进行语句构建,再返回语句。重点来了,这里的mappedStatements实例是一个Map对象(Configuration.StrictMap),泛型为<String, MappedStatement
protected final Map<String, MappedStatement> mappedStatements;
嗯,应该是根据传入的id到此Map中去获取对应的MappedStatement对象,遂查看此Map对象的初始化方法。搜了一下,应该是这里。
public void addMappedStatement(MappedStatement ms) {
this.mappedStatements.put(ms.getId(), ms);
}
打上断点,重启项目,bingo!捕获到了,F8走起,发现这里有一个循环,具体迭代里面的东西怎么来的就没看了,但是可以确定是扫描了项目中所有的Mapper文件得来的所有Mapper.xml中的id,并且这里是在前面加上了namespace的。
public MappedStatement addMappedStatement(String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId) {
id = this.applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = new org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType);
statementBuilder.resource(this.resource);
statementBuilder.fetchSize(fetchSize);
statementBuilder.statementType(statementType);
statementBuilder.keyGenerator(keyGenerator);
statementBuilder.keyProperty(keyProperty);
statementBuilder.keyColumn(keyColumn);
statementBuilder.databaseId(databaseId);
this.setStatementTimeout(timeout, statementBuilder);
this.setStatementParameterMap(parameterMap, parameterType, statementBuilder);
this.setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
this.setStatementCache(isSelect, flushCache, useCache, this.currentCache, statementBuilder);
MappedStatement statement = statementBuilder.build();
this.configuration.addMappedStatement(statement);
return statement;
}
接下来就是迭代循环时,发现一个有趣的现象。
public void addMappedStatement(MappedStatement ms) {
this.mappedStatements.put(ms.getId(), ms);
}
这里put方法,每次会放两个值到这个Map中,感到疑惑,点进去发现,如果id带namespace也就是带**.**,里面会将此id处理为两个字符串,一个是原本带namespace的,一个是经过截取只剩下最后的id的字符串,如com.abc.scInstance,这里产生两个字符串->{
com.abc.scInstance,
scInstance
}
得出结论,return (MappedStatement)this.mappedStatements.get(id);
这里的id不带namespace也是可以在mappedStatements中获取到对应的MappedStatement对象,进行匹配到对应的带有namespace的id的语句。
而为什么是要两个字符串,里面存相同数据,那应该是就是当不同Mapper文件中存在相同id时用于区分了,毕竟加上实体类的namespace得到的必定是唯一的语句。具体原因后续再研究下写上来。
总结:
作为新手的第一次原创源码解析,感觉自己的完全没有讲清楚的,如果有大牛路过,希望给出指导,在下感激不尽。 --写于踏上工作岗位的3个月后…