1.配置文件解析
1.1获取inputstream
InputStream xml = Resources.getResourceAsStream("mybatis-config-1.xml");
通过加载xml配置文件方式获取输入流,可以通过classloader的getResourceAsStream方法来加载,所以获取classloader就很关键,如下:
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[]{classLoader,defaultClassLoader,
Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(),systemClassLoader};
}
1.2XPathParser的出现和作用
1.2.1XPathParser构造完成以下事情:
- 在xPathParser中,通过XPathFactory.newInstance()构造出xpath用来解析xml值;
- DocumentBuilderFactory.newInstance()构造出DocumentBuilderFactory对象,
- 然后通过factory.newDocumentBuilder()得到DocumentBuilder对象
- 最后通过DocumentBuilder.parse解析输入流inpustream获取到Document
- 以上这些都是jdk中自带的完成XML加载,然后封装成mybatis的XpathParser
注意XMLConfigBuilder构造方法,里面会直接new Configuration(),这里面会
typeAliasRegistry.registerAlias很多别名
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(inputStream));
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
//调用factory 里面的一系列set方法,设置属性 此处省略.......
DocumentBuilder builder = factory.newDocumentBuilder();
//调用builder 里面的一系列set方法,设置属性 此处省略.......
return builder.parse(inputSource);
}
1.2.2 xpathparser的作用与XNode
- xPathParser主要是解析xml节点,通过它里面的xpath去执行的,xpath在它构造的时候实例化的;Xnode会传入
variables进行解析节点里面的变量值 - 如下常规解析节点方法
parseConfiguration(parser.evalNode("/configuration"));
public XNode evalNode(Object root, String expression) {
Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
if (node == null) {
return null;
}
return new XNode(this, node, variables);
}
public String evalString(Object root, String expression) {
String result = (String) evaluate(expression, root, XPathConstants.STRING);
result = PropertyParser.parse(result, variables);
return result;
}
private Object evaluate(String expression, Object root, QName returnType) {
try {
return xpath.evaluate(expression, root, returnType);
} catch (Exception e) {
throw new BuilderException("Error evaluating XPath. Cause: " + e, e);
}
}
1.3parseConfiguration解析配置文件各个节点
有了前面的XPathParser和Xnode就可以解析配置文件的各个节点值了
- 有涉及到包是如何被扫描的,然后注册成别名的; typeAliasesElement
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void XMLConfigBuilder#parseConfiguration(XNode root) {
try {
// issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
1.3.1 mapper.xml文件解析
mapperElement(root.evalNode("mappers"));
mybatis-config.xml中配置mappers方式种类如下:
<mappers>
<mapper resource="mapper/department.xml"/>
<!--
<mapper class="com.linkedbear.mybatis.mapper.UserMapper"/>
<package name="com.linkedbear.mybatis.mapper"/>
-->
</mappers>
1.3.1-1 resource属性方式的解析
通过构建XMLMapperBuilder(配置文件解析是构建XMLConfigBuilder),以mapper/department.xml作为resource构造输入流的方式,同样也是XPathParser,XPath,XNode的相互配合; 新出现一个MapperBuilderAssistant
private void mapperElement(XNode parent){
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
configuration, resource, sqlFragments);
}
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
super(configuration);
this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
this.parser = parser;
this.sqlFragments = sqlFragments;
this.resource = resource;
}
1.3.1-2 解析mapper.xml文件各个节点
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
1.3.1-2.1 设置命名空间
XPathParser已经是获取到mapper.xml文件了,所以获取到它的Xnode节点,直接获取命名空间属性,这玩意要写,无所谓内容,但是不能没有
public void XMLMapperBuilder#parse() {
configurationElement(parser.evalNode("/mapper")); }
void configurationElement(XNode context) {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
1.3.1-2.2 缓存解析
//XMLMapperBuilder#configurationElement
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
1.获取二级缓存设置属性
private void cacheElement(XNode context) {
if (context != null) {
String type = context.getStringAttribute("type", "PERPETUAL");
//省略N行.....
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
2.构建缓存对象
- 如果没有默认的缓存实现,则使用PerpetualCache,然后实例化它并为之设置属性,这属性是标签里面的子属性
- 如果是PerpetualCache缓存,如果存在decorators,则依次实例化他们且将Cache作为参数传入并且设置属性,然后进行标准的装饰缓存设置
- 如果是自定义的缓存实现,则套一层LoggingCache;非自定义缓存才会套装饰的那些缓存实现
public Cache build() {
setDefaultImplementations();
Cache cache = newBaseCacheInstance(implementation, id);
setCacheProperties(cache);
// issue #352, do not apply decorators to custom caches
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
cache = setStandardDecorators(cache);//装饰者模式构建属性对应的缓存
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
cache = new LoggingCache(cache); //直接封装成LoggingCache
}
return cache;
}
private Cache setStandardDecorators(Cache cache) {
try {
MetaObject metaCache = SystemMetaObject.forObject(cache);
if (size != null && metaCache.hasSetter("size")) {
metaCache.setValue("size", size);
}
if (clearInterval != null) {
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
if (readWrite) { //根据属性封装缓存实现
cache = new SerializedCache(cache); //如果非只读,则进行序列化后放入缓存
}
cache = new LoggingCache(cache);
cache = new SynchronizedCache(cache);
if (blocking) {
cache = new BlockingCache(cache);
}
return cache;
} catch (Exception e) {
throw new CacheException("Error building standard cache decorators. Cause: " + e, e);
}
}
最外层是BlockingCache-Synchronized-Logging-Serialized-Scheduled-LRU-Perpetaul
1.3.1-2.3 resultMap解析
resultMap常规写法例子如下:
<resultMap id="userMap" type="com.linkedbear.mybatis.entity.User">
<!-- <id property="id" column="id"/>-->
<constructor>
<idArg column="id" javaType="String" name="idd" />
<!--<arg column="name" javaType="String"/>-->
</constructor>
<result property="birthday" column="birthday"/>
<association property="department" javaType="com.linkedbear.mybatis.entity.Department">
<id property="id" column="department_id"/>
<result property="name" column="department_name"/>
</association>
</resultMap>
resultMapElements(context.evalNodes("/mapper/resultMap"));
1.封装的目标类型提取
这个type可以是别名哦,所有源码底层如果在别名列表没查到,直接就用class.forname方式加载了
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
Class<?> typeClass = resolveClass(type);
if (typeClass == null) {
typeClass = inheritEnclosingType(resultMapNode, enclosingType);
}
2.id 、result 、association 、collection 普通标签解析
拿到这些常规子节点的Xnode,就一顿解析这些标签上面的一些属性,比如name,property,column,javaType,jdbcType,select等等,最终封装成一个ResultMapping
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) {
String property;
if (flags.contains(ResultFlag.CONSTRUCTOR)) {
property = context.getStringAttribute("name");
} else {
property = context.getStringAttribute("property");
}
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
//中间省略N多行.....
String typeHandler = context.getStringAttribute("typeHandler");
String resultSet = context.getStringAttribute("resultSet");
Class<?> javaTypeClass = resolveClass(javaType);
Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
3.constructor标签解析
<constructor>
<idArg column="id" javaType="String" name="idd" />
<!--<arg column="name" javaType="String"/>-->
</constructor>
也是同样获取这个构造器的节点,最终也是调用buildResultMappingFromContext封装成一个ResultMapping,只是在它没有property属性,而是用name属性作为property
4.Discriminator的解析
这东西是用在根据列值的不同,映射不同的封装对象,不过它的封装使用builderAssistant.buildDiscriminator构造的是Discriminator
<resultMap id="userWithDiscriminator" type="com.linkedbear.mybatis.entity.User">
<discriminator column="deleted" javaType="boolean">
<case value="false" resultMap="userlazy"/>
<case value="true" resultType="com.linkedbear.mybatis.entity.User"/>
</discriminator>
</resultMap>
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) {
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<>();
for (XNode caseChild : context.getChildren()) {
String value = caseChild.getStringAttribute("value");
String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings, resultType));
discriminatorMap.put(value, resultMap);
}
return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
}
5.resultMap整体融合
继续解析继承过来的resultMap,融合前面解析好的resultMap和构造器以及Discriminator
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
1.3.1-2.4 sql片段解析
这边会涉及到dataBaseId的区分
sqlElement(context.evalNodes("/mapper/sql"));
private void sqlElement(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
sqlFragments.put(id, context);
}
}
}
1.3.1-2.5 CRUD的标签解析#XMLStatementBuilder
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
1.3.1-2.5-1 基础属性的解析获取
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
1.3.1-2.5-2 include标签的解析
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
1.3.1-2.5-3 自增主键的解析
processSelectKeyNodes(id, parameterTypeClass, langDriver);
1.3.1-2.5-4 SQL解析
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
//XMLLanguageDriver
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
return builder.parseScriptNode();
}
- 解析CRUD标签内的节点进行封装为SqlNode呈现为SqlSource
解析节点中的内容是否为动态的SQL即是否有${}以及动态标签
public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource;
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
protected MixedSqlNode parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<>();
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
XNode child = node.newXNode(children.item(i));
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
String data = child.getStringBody("");
TextSqlNode textSqlNode = new TextSqlNode(data);
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlerMap.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(child, contents);
isDynamic = true;
}
}
return new MixedSqlNode(contents);
}
- 如果不包含动态标签,会将SQL中的#变量,替换成?且封装对应的参数类型信息ParameterMappings(在prepareStatement中设置参数要用到),在RawSqlSource初始化的时候进行处理的,最终封装为了StaticSqlSource,里面的sql是可以直接用来给PrepareStatement执行的;所以非动态的SQL变量解析在解析阶段就已经处理完了;
public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
this(configuration, getSql(configuration, rootSqlNode), parameterType);
}
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> clazz = parameterType == null ? Object.class : parameterType;
sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
}
//SqlSourceBuilder
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
String sql;
if (configuration.isShrinkWhitespacesInSql()) {
sql = parser.parse(removeExtraWhitespaces(originalSql));
} else {
sql = parser.parse(originalSql);
}
return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
}
1.3.1-3 绑定Mapper接口给命名空间
xmlMapperBuilder.parse();
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
}
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// ignore, bound type is not required
}
if (boundType != null && !configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
1.3.1-3.1 构建mapper接口代理
如果mamper.xml里面的命名空间是个全路径类型,则会进行类型的加载,如1.3.1-3
public <T> void Configuration#addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
1.3.1-3.1.1.mapper接口代理创建
knownMappers.put(type, new MapperProxyFactory<>(type));
//MapperProxyFactory
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
1.3.1-3.1.2.mapper接口执行的InvocationHandler
这边会根据接口方法是默认还是原本的,如果是原本的则是构建PlainMethodInvoker,最终执行的是MapperMethod类
public class MapperProxy<T> implements InvocationHandler, Serializable {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
2.一级缓存和二级缓存原理
2.1一级缓存 基于BaseExecutor#localCache
一级缓存是基于SqlSession级别的,
因为 PerpetualCache localCache 是BaseExecutor类的成员变量,是作用在Executor上的,
而Executor是在每次opensession时候进行实例化的,所以只存在当前的 SqlSession 内有效,
不同的 SqlSession 内的localCache对象不同,
最后sqlSession.close的时候会去clearLocalCache的,所以一级缓存才不共享;
2.1.1 使用过程
查询数据库之前,先查询下一级缓存localCache是否有,有则直接返回
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
//省略N行......
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
//省略N行......
return list;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
2.2二级缓存基于namespace
2.2.1二级缓存实例化
二级缓存是在mapper.xml文件里面配置的cache标签,所以在解析mapper的时候,会进行设置到对应的MappedStatement,所以在currentNamespace下全局就一个cache对象,这个cache对象在查询的时候会被取出来,所以是夸相同命名空间下的sqlsession共享,而一级缓存使用的executor里面的cached对象
2.2.2查询数据前的过程
二级缓存的的使用,是在查询的时候,先查询二级缓存是否存在,通过TransactionalCacheManager.getObject去获取的,里面是委托TransactionalCache去执行的
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache(); //这边取出来的是MappedStatement中设置的cache
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
--------------------------------------------------------------------------
public Object TransactionalCacheManager#getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
private TransactionalCache TransactionalCacheManager#getTransactionalCache(Cache cache) {
return transactionalCaches.computeIfAbsent(cache, TransactionalCache::new);
}
public Object TransactionalCache#getObject(Object key) {
//委托查询的是使用cache对象即在mapperxml解析设置的那个cache
Object object = delegate.getObject(key);
if (object == null) { //没查询到对象,则将key加入set集合
entriesMissedInCache.add(key);
}
// issue #146
if (clearOnCommit) {
return null;
} else {
return object;
}
}
//TransactionalCache构造方法,最终是委托了cache对象去执行的
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<>();
this.entriesMissedInCache = new HashSet<>();
}
2.2.3查询数据后的过程
查询后,是将数据(key->data)放入TransactionalCache对象中的entriesToAddOnCommit(Map对象),在SqlSession没关闭前是没放入二级缓存中(cache)而是放在transactionCache的entriesToAddOnCommit中, 即用tcm.getObject是查询不到的
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list);
}
public void TransactionalCacheManager#putObject(Cache cache, CacheKey key, Object value) {
getTransactionalCache(cache).putObject(key, value);
}
public void TransactionalCache#putObject(Object key, Object object) {
entriesToAddOnCommit.put(key, object);
}
2.3Sqlsession.close的过程
2.3.1 开启二级缓存
sqlsession在关闭的时候,会进行mybatis的事务提交,然后会执行TransactionalCache中的commit,这个对象里面有个map类型的对象entriesToAddOnCommit,它存储的可是查询后的数据key为cachekey,value是数据。
因为二级缓存是夸SqlSession,所以在Sqlsession关闭的时候才进行事务提交,才会把当前sqlsession里面存储的数据存入到二级缓存对象中,防止中间有问题,把当前sqlsession有问题数据存入到缓存,造成其他人使用的时候出现脏数据;
//Sqlssion关闭
public void SqlSession#close() {
try {
executor.close(isCommitOrRollbackRequired(false));
closeCursors();
dirty = false;
} finally {
ErrorContext.instance().reset();
}
}
//CachingExecutor#
public void close(boolean forceRollback) {
try {
if (forceRollback) {
tcm.rollback();
} else {
tcm.commit();//事务提交
}
} finally {
delegate.close(forceRollback);
}
}
//CachingExecutor.delegate ---> BaseExecutor
public void close(boolean forceRollback) {
try {
rollback(forceRollback);
} finally {
if (transaction != null) {
transaction.close();
}
}
}
//BaseExecutor
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
clearLocalCache(); //清空一级缓存
flushStatements(true);
} finally {
if (required) {
transaction.rollback();
}
}
}
}
//TransactionalCacheManager
public void commit() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
//TransactionalCache
public void commit() {
if (clearOnCommit) {
delegate.clear();
}
flushPendingEntries();
reset();
}
private void flushPendingEntries() { //TransactionalCache
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
3.事务
3.1事务配置
<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://localhost:3306/mybatis?characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
3.2事务配置解析
environmentsElement(root.evalNode("environments"));
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) { //会区分不同的开发环境配置
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
//直接无参构造实例化对象,用type从别名列表里面取值进行实例化
TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
3.3事务使用的位置
3.3.1在openSession的时候会进行事务初始的设置,然后传入到Executor中
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
//这边获取的是根据type来创建的transaction对象
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
//传给了Eexcector
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
3.3.2事务提交回滚
@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
clearLocalCache();
flushStatements();
if (required) {
transaction.commit();
}
}
@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
clearLocalCache();
flushStatements(true);
} finally {
if (required) {
transaction.rollback();
}
}
}
}
4.Interceptor
4.1拦截器编写规范和配置
要编写一个插件需实现Interceptor接口的类,然后在类上要写上注解Intercepts,然后标注要拦截的类和方法,然后在config.xml文件中配置这个插件类,拦截器的处理的业务是编写在interceptor.intercept方法里面的
@Intercepts(@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class CustomInterceptor implements Interceptor {
<plugins>
<!--<plugin interceptor="com.linkedbear.mybatis.plugin.CustomInterceptor"/>-->
<plugin interceptor="com.linkedbear.mybatis.plugin.PerformanceInterceptor">
<!-- 最大容忍时间 -->
<property name="maxTolerate" value="10"/>
</plugin>
</plugins>
4.2配置解析
pluginElement(root.evalNode("plugins"));
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
4.3作用位置
4.3.1添加配置的拦截器信息
一个是注解解析,一个是配置文件解析,进行解析插件,然后添加到interceptor
4.3.2mybatis自己的拦截切入点
mybatis这四个位置进行拦截包装,Executor,statementHandler,parameterHandler,resultSetHandler
4.3.2.1 解析拦截器配置的注解参数
获取拦截器类上的注解Intercepts,然后获取里面的需要拦截的接口和方法,封装为声明的接口和方法集合对应关系
@Intercepts(@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}))
public class CustomInterceptor implements Interceptor {
------------------------------------------------------------------------------------------
private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {
Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);
// issue #251
if (interceptsAnnotation == null) {
throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());
}
Signature[] sigs = interceptsAnnotation.value();
Map<Class<?>, Set<Method>> signatureMap = new HashMap<>();
for (Signature sig : sigs) {
Set<Method> methods = signatureMap.computeIfAbsent(sig.type(), k -> new HashSet<>());
try {
Method method = sig.type().getMethod(sig.method(), sig.args());
methods.add(method);
} catch (NoSuchMethodException e) {
throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);
}
}
return signatureMap;
}
4.3.2.2 对配置的拦截接口创建jdk代理
Plugin.wrap(target, this);
public static Object wrap(Object target, Interceptor interceptor) {
//获取当前拦截器上配置的拦截接口,以key为接口,value为方法集合的map返回
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
return Proxy.newProxyInstance(type.getClassLoader(),interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
//动态代理执行的时候触发interceptor.intercept
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
//拦截器的核心业务逻辑位子
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
4.4 分页插件pageHelper
关键类 PageInterceptor PageHelper
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.3.2</version>
</dependency>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="debug" value="true"/>
<property name="helperDialect" value="mysql"/>
</plugin>
</plugins>
4.4.1 拦截器声明
@Intercepts(
{
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
}
)
public class PageInterceptor implements Interceptor {
4.4.2 拦截器properties设置
@Override
public void setProperties(Properties properties) {
//缓存 count ms
msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
String dialectClass = properties.getProperty("dialect");
if (StringUtil.isEmpty(dialectClass)) {
dialectClass = default_dialect_class;
}
try {
//加载方言的实现
Class<?> aClass = Class.forName(dialectClass);
dialect = (Dialect) aClass.newInstance();
} catch (Exception e) {
throw new PageException(e);
}
//设置方言的属性
dialect.setProperties(properties);
.....省略N行
}
4.4.2 拦截器invoke逻辑
@Override
public Object intercept(Invocation invocation) throws Throwable {
try {
//......省略获取参数
checkDialectExists();
//对 boundSql 的拦截处理
if (dialect instanceof BoundSqlInterceptor.Chain) {
boundSql = ((BoundSqlInterceptor.Chain) dialect).doBoundSql(BoundSqlInterceptor.Type.ORIGINAL, boundSql, cacheKey);
}
List resultList;
//调用方法判断是否需要进行分页,如果不需要,直接返回结果
if (!dialect.skip(ms, parameter, rowBounds)) {
//开启debug时,输出触发当前分页执行时的PageHelper调用堆栈
// 如果和当前调用堆栈不一致,说明在启用分页后没有消费,当前线程再次执行时消费,调用堆栈显示的方法使用不安全
debugStackTraceLog();
//判断是否需要进行 count 查询
if (dialect.beforeCount(ms, parameter, rowBounds)) {
//查询总数
Long count = count(executor, ms, parameter, rowBounds, null, boundSql);
//处理查询总数,返回 true 时继续分页查询,false 时直接返回
if (!dialect.afterCount(count, parameter, rowBounds)) {
//当查询总数为 0 时,直接返回空的结果
return dialect.afterPage(new ArrayList(), parameter, rowBounds);
}
}
resultList = ExecutorUtil.pageQuery(dialect, executor,
ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
} else {
//rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
return dialect.afterPage(resultList, parameter, rowBounds);
} finally {
if (dialect != null) {
dialect.afterAll();
}
}
}
4.4.2.1 分页总数的count的实现
4.4.2.1-1 执行自定义的count实现
//获取count查询语句的id
String countMsId = countMsIdGen.genCountMsId(ms, parameter, boundSql, countSuffix);
//先判断是否存在手写的 count 查询
MappedStatement countMs = ExecutorUtil.getExistedMappedStatement(ms.getConfiguration(), countMsId);
if (countMs != null) {
count = ExecutorUtil.executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
//--------------------------------------------------------------------------------------
public static Long executeManualCount(Executor executor, MappedStatement countMs,
Object parameter, BoundSql boundSql,
ResultHandler resultHandler) throws SQLException {
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
BoundSql countBoundSql = countMs.getBoundSql(parameter);
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
//某些数据(如 TDEngine)查询 count 无结果时返回 null
if (countResultList == null || ((List) countResultList).isEmpty()) {
return 0L;
}
return ((Number) ((List) countResultList).get(0)).longValue();
}
4.4.2.1-2 执行自动生成的count的mapperstatement执行
这边会根据不同的sql语句解析生成count的sql,通过countSqlParser.getSmartCountSql实现
countMs = MSUtils.newCountMappedStatement(ms, countMsId);//构建count的mappedStatement
count = ExecutorUtil.executeAutoCount(this.dialect, executor, countMs, parameter, boundSql, rowBounds, resultHandler);
public static Long executeAutoCount(Dialect dialect, Executor executor, MappedStatement countMs,
Object parameter, BoundSql boundSql,
RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
Map<String, Object> additionalParameters = getAdditionalParameter(boundSql);
//创建 count 查询的缓存 key
CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
//调用方言获取 count sql
String countSql = dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
//countKey.update(countSql);
BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
//当使用动态 SQL 时,可能会产生临时的参数,这些参数需要手动设置到新的 BoundSql 中
for (String key : additionalParameters.keySet()) {
countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
//对 boundSql 的拦截处理
if (dialect instanceof BoundSqlInterceptor.Chain) {
countBoundSql = ((BoundSqlInterceptor.Chain) dialect).doBoundSql(BoundSqlInterceptor.Type.COUNT_SQL, countBoundSql, countKey);
}
//执行 count 查询
Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
//某些数据(如 TDEngine)查询 count 无结果时返回 null
if (countResultList == null || ((List) countResultList).isEmpty()) {
return 0L;
}
return ((Number) ((List) countResultList).get(0)).longValue();
}
4.4.2.2 分页查询实现
通过对应的dialect实现获取对应的分页sql,然后重新构建BoundSql进行查询
resultList = ExecutorUtil.pageQuery(dialect, executor,
ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
public static <E> List<E> pageQuery(Dialect dialect, Executor executor, MappedStatement ms, Object parameter,
RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql, CacheKey cacheKey) throws SQLException {
//判断是否需要进行分页查询
if (dialect.beforePage(ms, parameter, rowBounds)) {
//生成分页的缓存 key
CacheKey pageKey = cacheKey;
//处理参数对象
parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey);
//调用方言获取分页 sql
String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), pageSql, boundSql.getParameterMappings(), parameter);
Map<String, Object> additionalParameters = getAdditionalParameter(boundSql);
//设置动态参数
for (String key : additionalParameters.keySet()) {
pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
}
//对 boundSql 的拦截处理
if (dialect instanceof BoundSqlInterceptor.Chain) {
pageBoundSql = ((BoundSqlInterceptor.Chain) dialect).doBoundSql(BoundSqlInterceptor.Type.PAGE_SQL, pageBoundSql, pageKey);
}
//执行分页查询
return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
} else {
//不执行分页的情况下,也不执行内存分页
return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
}
}
5.日志
5.1 配置
如果要指定具体的日志实现,则需要在配置文件中进行配置;默认在
<settings>
<!-- <setting name="logImpl" value="LOG4J"/>-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="cacheEnabled" value="true"/>
</settings>
5.2 配置解析
//XmlConfigBuilder
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomLogImpl(settings);
private void loadCustomLogImpl(Properties props) {
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));//解析别名
configuration.setLogImpl(logImpl);
}
//configuration
public void setLogImpl(Class<? extends Log> logImpl) {
if (logImpl != null) {
this.logImpl = logImpl;
LogFactory.useCustomLogging(this.logImpl);
}
}
//LogFactory
public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
setImplementation(clazz);
}
//LogFactory
private static void setImplementation(Class<? extends Log> implClass) {
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
Log log = candidate.newInstance(LogFactory.class.getName());
logConstructor = candidate;
}
//默认加载以下存在的任何一个
static {
tryImplementation(LogFactory::useSlf4jLogging);
tryImplementation(LogFactory::useCommonsLogging);
tryImplementation(LogFactory::useLog4J2Logging);
tryImplementation(LogFactory::useLog4JLogging);
tryImplementation(LogFactory::useJdkLogging);
tryImplementation(LogFactory::useNoLogging);
}
//LogFactory
public static Log getLog(Class<?> clazz) {
return getLog(clazz.getName());
}
//LogFactory
public static Log getLog(String logger) {
return logConstructor.newInstance(logger);
}
5.3 其他日志增强类
5.3.1 ConnectionLogger#InvocationHandler
这是数据库连接的日志类,它是个inovcationHandler
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler
5.3.1-1 使用的位置
在获取数据库连接的时候,对connectoin接口进行代理的增加
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
5.3.1-2 代理核心逻辑
prepareStatement/prepareCall/createStatement方法执行的触发代理执行
@Override
public Object invoke(Object proxy, Method method, Object[] params)
throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
}
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else if ("createStatement".equals(method.getName())) {
Statement stmt = (Statement) method.invoke(connection, params);
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else {
return method.invoke(connection, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
5.3.2 PreparedStatementLogger#InvocationHandler
这个代理的接口是PreparedStatement,是在执行ConnectionLog#invoke初始化的,看5.3.1-2
public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLog, int queryStack) {
InvocationHandler handler = new PreparedStatementLogger(stmt, statementLog, queryStack);
ClassLoader cl = PreparedStatement.class.getClassLoader();
return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class, CallableStatement.class}, handler);
}
5.3.2-1 核心逻辑invoke
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
if (EXECUTE_METHODS.contains(method.getName())) {
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true);
}
clearColumnInfo();
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);
}
} else if (SET_METHODS.contains(method.getName())) {
if ("setNull".equals(method.getName())) {
setColumn(params[0], null);
} else {
setColumn(params[0], params[1]);
}
return method.invoke(statement, params);
} else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else if ("getUpdateCount".equals(method.getName())) {
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
5.Mybatis整合spring
SqlSessionFactoryBean
5.1 SqlSessionFactory构建过程
@Override
public void afterPropertiesSet() throws Exception {
this.sqlSessionFactory = buildSqlSessionFactory();
}
5.1.1 Configuration构建
如果配置了configLocation参数,则通过XmlConfigBuilder方式加载inputStream,里面也肯定是直接new一个Configuration
final Configuration targetConfiguration;
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configuration != null) {
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
targetConfiguration = new Configuration();
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
}
5.1.2 扫描别名注解的包,注册别名类
if (hasLength(this.typeAliasesPackage)) {
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
.filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
.filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
}
if (!isEmpty(this.typeAliases)) {
Stream.of(this.typeAliases).forEach(typeAlias -> {
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
});
}
5.1.3 处理插件、类型处理器
if (!isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach(plugin -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
});
}
if (hasLength(this.typeHandlersPackage)) {
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
}
if (!isEmpty(this.typeHandlers)) {
Stream.of(this.typeHandlers).forEach(typeHandler -> {
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
});
}
targetConfiguration.setDefaultEnumTypeHandler(defaultEnumTypeHandler);
5.1.4 添加缓存
跟解析缓存标签</Cache>后,将缓存添加到configuration中类似
Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
5.1.5 解析mybatis的配置文件
如果配置了configlocation,则就会构建XmlConfigBuilder,就要解析配置文件了
xmlConfigBuilder.parse();
5.1.5 处理数据源和事务工厂
在解析mybatis全局文件的时候,事务工厂是通过type指定进行加载的,如果没配置,则这边使用SpringManagedTransactionFactory
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
5.1.6 xxxMaper.xml文件加载解析
mapperLocations是Resource对象,那就只能配置classpath:mapper/*.xml形式了
private Resource[] mapperLocations;
for (Resource mapperLocation : this.mapperLocations) {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
}
}
}
5.1.7 构建DefaultSqlSessionFactory
new SqlSessionFactoryBuilder().build(targetConfiguration)
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
5.2 Mapper接口注入SpringIOC
在没有整合spring的时候,我们获取mapper对象是通过Sqlsession.getMapper,底层是从mapperRegistry拿的,它是在解析xxxMapper.xml文件时候进行addMapper进去的。
如果整合了spring,获取mapper是从容器拿的,所以得有个东西把mapper接口给扫描出来注入到IOC中
5.2.1 Mapper接口扫描(MapperScannerConfigurer)
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
5.2.1-1 获取basePackage包下的类转BeanDefinition
protected Set<BeanDefinitionHolder> ClassPathBeanDefinitionScanner#doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
5.2.1-2 解析BeanDefination
private void ClassPathMapperScanner#processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed)
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
}
}
5.2.1-2.1 设置beanDefination的beanClass成mapperFactoryBeanClass
- mapperFactoryBeanClass构造参数是mapperInterface,所以要将beanName设置到构造参数进去,在MapperFactoryBean进行实例化的时候会设置这个参数,这样mapperInterface就进来
- 在beanDefination中加入sqlsessionFactory对象,这样实例化的时候也会设置到mapperFactoryBean里面
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory",
new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
5.2.2 MapperFactoryBean初始化加载mapper接口到configuration中
MapperFactoryBean继承了DaoSupport,它实现了InitializingBean接口,在afterProperties方法中进行了mapperInterface的加载
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T>
public abstract class SqlSessionDaoSupport extends DaoSupport
public abstract class DaoSupport implements InitializingBean{
@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
// Let abstract subclasses check their configuration.
checkDaoConfig();
//-------------------------------------------------
@Override
protected void MapperFactoryBean#checkDaoConfig() {
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
configuration.addMapper(this.mapperInterface);
}
}
5.3 从IOC容器获取Mapper对象
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring-mybatis.xml");
DepartmentMapper departmentMapper = ctx.getBean(DepartmentMapper.class);
5.3.1 MapperFactoryBean取mapper对象
因为mapper接口在被spring扫描的时候,都转成了MapperFactoryBean了(5.2.1-2.1),它是个FactoryBean,所以获取对象走就是getObject
@Override
public T MapperFactoryBean#getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
55.Mybatis-plus整合spring(待续)
使用plus,如果只是CRUD,你只要定义Mapper接口,不用定义对应的xml文件就能实现功能;
它对mybaits的关键类都进行了继承,如:
MybatisSqlSessionFactoryBean、MybatisConfiguration、MybatisMapperRegistry、
MybatisSqlSessionFactoryBuilder、MybatisMapperProxyFactory
55.1 SqlSessionFactory构建过程
plus在构建SqlSeesionFactory过程,只是把mybatis的一些关键类替换成自己的定义
6.mybatis整体流程
6.1 全局配置文件加载
6.1.1 classLoader加载文件
classLoaderWrapper.getResourceAsStream(resource, loader);
6.1.2 解析配置文件#XMLConfigBuilder
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
6.2 SqlSession的执行
6.2.1 sqlsession创建
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
6.2.1-1 构建事务Transaction
在全局解析配置文件的时候,构建enviroment对象的时候设置了transactionFactory,所以通过它可以获取到,再构建Transaction
6.2.1-2 构建Executor并增强
默认的cacheEnabled是true,可以在set标签里面设置这个属性;这个是控制数据可以存入二级缓存,从这边看,如果你没有在mapper.xml里面开启二级缓存,这个也可以设置为false
最后会对Executor进行增加插件的增强
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
6.2.1-3 构建SqlSession
有了configuration和Executor对象,就可以构建sqlsession对象了,在具体进行CRUD的时候,都是委托给executor去执行的
return new DefaultSqlSession(configuration, executor, autoCommit);
6.3 Mapper的代理执行
在解析Mapper.xml文件的时候,XMLMapperBuilder会将命名空间的类型加入到knownMappers,加入的是MapperProxyFactory类型,在获取Sqlsession获取mapper的时候,获取就是mapperProxyFactory类型
knownMappers.put(type, new MapperProxyFactory<>(type));
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
6.3.1 Mapper的执行CRUD先走代理mapperProxy#invoke
- 对Mapper接口进行了jdk代理,执行的invokerHandler逻辑就是MapperProxy,所以当你的mapper这样调用时userMapper.getUesrName(),就进入了MapperProxy类的invoke方法了
- 在MapperProxy#invoke里面又包装了一层PlainMethodInvoker和MapperMethod
- 所以最终会进入MapperMethod这个类,执行它的excute方法
mapperProxy#invoke --> PlainMethodInvoker#invoke-->MapperMethod#execute
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
//MapperProxy
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
MapperMethodInvoker invoker = methodCache.get(method);
if (invoker != null) {
return invoker;
}
return methodCache.computeIfAbsent(method, m -> {
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
6.4 Mapper接口方法属性解析
6.4.1 返回值和参数名解析
- 在MapperMethod对象构建的时候,会解析当前调用方法method返回值和参数相关属性,如返回值可以是void,collection,optional,Cursor,参数类型也可以是RowBounds,ResultHandler
- 参数名的解析默认是按照0->arg0,1->arg1来对应的;如果有写@param参数注解则是名称对应
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
//参数类型,返回值解析
this.method = new MethodSignature(config, mapperInterface, method);
}
public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
if (resolvedReturnType instanceof Class<?>) {
this.returnType = (Class<?>) resolvedReturnType;
} else if (resolvedReturnType instanceof ParameterizedType) {
this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
} else {
this.returnType = method.getReturnType();
}
this.returnsVoid = void.class.equals(this.returnType);
this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
this.returnsCursor = Cursor.class.equals(this.returnType);
this.returnsOptional = Optional.class.equals(this.returnType);
this.mapKey = getMapKey(method);
this.returnsMap = this.mapKey != null;
this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
//参数解析
this.paramNameResolver = new ParamNameResolver(configuration, method);
}
6.4.1.1 mapper方法参数名解析封装
public interface UserMapper {
List<User> findAll( String id,String name);
执行上面的方法后,会最终进入MapperMethod类的execute方法,在执行CRUD的之前都会有一个参数的提取封装,要么返回一个Map,要么参数值
Object param = MethodSignature.convertArgsToSqlCommandParam(args);
6.4.1.1-1 参数多个且无@Param注解
List<User> findAll(List<String> id, String name);
6.4.1.1-2 参数多个且有@Param注解
List<User> findAll(@Param("id") String id, @Param("name")String name);
6.4.1.1-3 参数多个且有@Param注解且刚好是param1,param2
List<User> findAll(@Param("param1") String id, @Param("param2")String name);
6.4.1.1-4 参数一个且无@Param注解
- 如果无注解且单个参数集合或者数组类型,则是如此形式
- 无注解且非集合数组类型,则直接返回对应的值
- 参数有注解,则直接走多个参数的处理逻辑
6.4 获取BoundSql
BoundSql的获取是通过SqlSource去获取的,SqlSource的封装在解析Sql时候已经构建了(看1.3.1-2.5-4),分为DynamicSqlSource和RawSqlSource(StaticSqlSource)
6.4.1 StaticSqlSource获取BoundSql
直接封装BoundSql
public class StaticSqlSource implements SqlSource {
@Override
public BoundSql getBoundSql(Object parameterObject) {
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
6.4.2 DynamicSqlSource获取BoundSql
6.4.2.1 执行封装好的SqlNode
在SQL解析阶段,会对动态SQL标签解析为对应的SqlNode,如TrimSqlNode,IfSqlNode. 在方法执行阶段就直接将这些封装好的SqlNode进行执行,这边也会对#{}变量进行替换?以及解析变量名相关信息包括指定了jdbctype,与StaticSqlSource那边的解析一致
public class DynamicSqlSource implements SqlSource {
@Override
public BoundSql getBoundSql(Object parameterObject) {
DynamicContext context = new DynamicContext(configuration, parameterObject);
//执行封装好的sqlNode
rootSqlNode.apply(context);
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
context.getBindings().forEach(boundSql::setAdditionalParameter);
return boundSql;
}
6.5 创建缓存的key
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
6.6 StatementHandler的构建
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(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
6.6.1 构建统一的RoutingStatementHandler并插件代理
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
6.6.2 StatementHandler的处理
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
6.6.2-1 获取数据库连接
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
6.6.2-2 handler准备
6.6.2-2.1实例化handler
connection.prepareStatement(sql);
6.6.2-2.2设置handler查询时间
stmt.setQueryTimeout(queryTimeout);
6.6.2-2.3setFetchSize
stmt.setFetchSize(fetchSize);
6.6.2-3 handler的参数化设置
获取boundSql中的parametersMapping参数进行一一设置对应的值和类型
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) {
jdbcType = configuration.getJdbcTypeForNull();
}
try {
typeHandler.setParameter(ps, i + 1, value, jdbcType);
} catch (TypeException | SQLException e) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
}
}
}
}
}
6.7 结果集封装
6.7.1 封装ResultSet获取字段信息
获取到对应的字段名,字段对应的JDBC类型和Java类型
ResultSetWrapper rsw = getFirstResultSet(stmt);
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
super();
this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
this.resultSet = rs;
final ResultSetMetaData metaData = rs.getMetaData();
final int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
classNames.add(metaData.getColumnClassName(i));
}
}
6.7.2 处理单个结果集映射即非嵌套映射
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
throws SQLException {
DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
ResultSet resultSet = rsw.getResultSet();
skipRows(resultSet, rowBounds);
while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
}
}
6.7.2.1 创建实体空对象
Object resultObject = createResultObject(rsw, resultMap, constructorArgTypes, constructorArgs, columnPrefix);
6.7.2.1 设置在resultMap中未指定的属性值
找出在resultMap未进行声明映射的属性,通过反射设置值,映射列名来自resultSetWrapper里面的封装好的列名集合
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//为声明的列名映射属性
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
//找出在resultMapp中声明和未声明的属性集合
private void loadMappedAndUnmappedColumnNames(ResultMap resultMap, String columnPrefix) throws SQLException {
List<String> mappedColumnNames = new ArrayList<>();
List<String> unmappedColumnNames = new ArrayList<>();
final String upperColumnPrefix = columnPrefix == null ? null : columnPrefix.toUpperCase(Locale.ENGLISH);
final Set<String> mappedColumns = prependPrefixes(resultMap.getMappedColumns(), upperColumnPrefix);
for (String columnName : columnNames) {
final String upperColumnName = columnName.toUpperCase(Locale.ENGLISH);
if (mappedColumns.contains(upperColumnName)) {
mappedColumnNames.add(upperColumnName);
} else {
unmappedColumnNames.add(columnName);
}
}
mappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), mappedColumnNames);
unMappedColumnNamesMap.put(getMapKey(resultMap, columnPrefix), unmappedColumnNames);
}
6.7.2.2 设置在resultMap中指定的属性值
private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, 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) {
String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
column = null;
}
if (propertyMapping.isCompositeResult()
|| (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))
|| propertyMapping.getResultSet() != null) {
Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
// issue #541 make property optional
final String property = propertyMapping.getProperty();
if (property == null) {
continue;
} else if (value == DEFERRED) {
foundValues = true;
continue;
}
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
// gcode issue #377, call setter on nulls (value is not 'found')
metaObject.setValue(property, value);
}
}
}
return foundValues;
}
6.7.2.3 将解析好的一行数据存储到DefaultResultHandler
storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
linkToParents(rs, parentMapping, rowValue);
} else {
callResultHandler(resultHandler, resultContext, rowValue);
}
}
@SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
resultContext.nextResultObject(rowValue);
((ResultHandler<Object>) resultHandler).handleResult(resultContext);
}
@Override
public void handleResult(ResultContext<?> context) {
list.add(context.getResultObject());
}