Mybatis源码分析
一、整体架构图
二、核心组件
Configuration:用于描述 MyBatis 主配置文件信息,MyBatis 框架在启动时会加载主配置文件,将配置信息转换为 Configuration 对象。
SqlSession:面向用户的 API,是 MyBatis 与数据库交互的接口。
Executor:SQL 执行器,用于和数据库交互。SqlSession 可以理解为 Executor 组件的外观(外观模式),真正执行 SQL 的是 Executor 组件。
MappedStatement:用于描述 SQL 配置信息,MyBatis 框架启动时,XML 文件或者注解配置的 SQL信息会被转换为 MappedStatement 对象注册到 Configuration 组件中。
StatementHandler:封装了对 JDBC 中 Statement 对象的操作,包括为 Statement 参数占位符设置值,通过 Statement 对象执行 SQL语句。
TypeHandler:类型处理器,用于 Java 类型与 JDBC 类型之间的转换。
ParameterHandler:用于处理 SQL 中的参数占位符,为参数占位符设置值。
ResultSetHandler:封装了对 ResultSet 对象的处理逻辑,将结果集转换为 Java 实体对象。
三、源码分析
从代码流程可以看出mybatis流程是这样的:
1、从全局配置文件(mybatis-config.xml)获取sqlSessionFactory对象
2、根据sqlSessionFactory获取SqlSession对象
3、获取mapper的实现类对象(MapperProxy)
4、执行增删改查方法
SqlSessionFactory的获取:
加载配置文件
解析配置文件,并将配置文件的信息封装在Configuration对象中
根据Configuration创建SqlSessionFactory并返回
new SqlSessionFactoryBuilder.build(intputstream)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5; try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally {
ErrorContext.instance().reset(); try {
inputStream.close(); } catch (IOException var13) {
}
}return var5;}
build函数首先会构造一个XMLConfigBilder对象,从名字上大致可以猜到,该对象是用来解析XML配置文件的。
补充:
XMLxxxBuilder都是用来解析配置文件的,不同的XMLxxxBuilder用来解析mybatis配置文件的不同部位。比如:XMLConfigBuilder用来解析Mybatis的配置文件,XMLMapperBuilder用来解析Mybatis中的映射文件,XMLStatementBuilder用来解析映射文件的SQL语句。
这些XMLxxxBuilder都有一个共同的父类—BaseBuilder,这个父类维护了一个全局的Configuration对象,mybatis的配置文件解析后就以Configuration对象的形式存储。
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);}
1、创建XMLMapperEntityResolver对象
new XMLMapperEntityResolver(); 就是将Mybatis的DTD文件加载到一个私有集合中
private static final Map doctypeMap = new HashMap(); 并向外提供一个用户获取DTD的InputSource的方法
public InputSource resolveEntity(String publicId, String systemId);
2、创建XPathParser对象
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
this.commonConstructor(validation, variables, entityResolver); this.document = this.createDocument(new InputSource(inputStream));}
XPathParser创建完成之后,回到真正执行XMLConfigBuilder创建的方法:
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration()); this.localReflectorFactory = new DefaultReflectorFactory(); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser;}
初始化Configuration对象
public Configuration() {
this.safeResultHandlerEnabled = true; this.multipleResultSetsEnabled = true; this.useColumnLabel = true; this.cacheEnabled = true; this.useActualParamName = true; this.localCacheScope = LocalCacheScope.SESSION; this.jdbcTypeForNull = JdbcType.OTHER; this.lazyLoadTriggerMethods = new HashSet(Arrays.asList("equals", "clone", "hashCode", "toString")); this.defaultExecutorType = ExecutorType.SIMPLE; this.autoMappingBehavior = AutoMappingBehavior.PARTIAL; this.autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE; this.variables = new Properties(); this.reflectorFactory = new DefaultReflectorFactory(); this.objectFactory = new DefaultObjectFactory(); this.objectWrapperFactory = new DefaultObjectWrapperFactory(); this.lazyLoadingEnabled = false; this.proxyFactory = new JavassistProxyFactory(); this.mapperRegistry = new MapperRegistry(this); this.interceptorChain = new InterceptorChain(); this.typeHandlerRegistry = new TypeHandlerRegistry(this); this.typeAliasRegistry = new TypeAliasRegistry(); this.languageRegistry = new LanguageDriverRegistry(); this.mappedStatements = (new Configuration.StrictMap("Mapped Statements collection")).conflictMessageProducer((savedValue, targetValue) -> {
return ". please check " + savedValue.getResource() + " and " + targetValue.getResource(); }); this.caches = new Configuration.StrictMap("Caches collection"); this.resultMaps = new Configuration.StrictMap("Result Maps collection"); this.parameterMaps = new Configuration.StrictMap("Parameter Maps collection"); this.keyGenerators = new Configuration.StrictMap("Key Generators collection"); this.loadedResources = new HashSet(); this.sqlFragments = new Configuration.StrictMap("XML fragments parsed from previous mappers"); this.incompleteStatements = new LinkedList(); this.incompleteCacheRefs = new LinkedList(); this.incompleteResultMaps = new LinkedList(); this.incompleteMethods = new LinkedList(); this.cacheRefMap = new HashMap(); this.typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); this.typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); this.typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); this.typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); this.typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); this.typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); this.typeAliasRegistry.registerAlias("FIFO", FifoCache.class); this.typeAliasRegistry.registerAlias("LRU", LruCache.class); this.typeAliasRegistry.registerAlias("SOFT", SoftCache.class); this.typeAliasRegistry.registerAlias("WEAK", WeakCache.class); this.typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class); this.typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); this.typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); this.typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); this.typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); this.typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class); this.typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class); this.typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class); this.typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class); this.typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); this.typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); this.typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class); this.languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); this.languageRegistry.register(RawLanguageDriver.class);}
当创建XMLConfigBuilder对象时,就会初始化Configuration对象,并且在初始化Configuration对象的时候,一些别名会被注册到Configuration的typeAliasRegistry容器中
解析配置文件:
当有了XMLConfigBuilder对象之后,接下来就可以用他来解析配置文件了
public Configuration parse() {
//防止parse()方法被一个实例多次调用
if (this.parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } else {
this.parsed = true;
// XPathParser.evalNode():创建表示configuration节点的XNode对象 // parseConfiguration():对XNode进行处理
this.parseConfiguration(this.parser.evalNode("/configuration")); return this.configuration; }
}
parseConfiguration(XNode root) 方法
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties")); Properties settings = this.settingsAsProperties(root.evalNode("settings")); this.loadCustomVfs(settings); this.loadCustomLogImpl(settings); this.typeAliasesElement(root.evalNode("typeAliases")); this.pluginElement(root.evalNode("plugins")); this.objectFactoryElement(root.evalNode("objectFactory")); this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); this.reflectorFactoryElement(root.evalNode("reflectorFactory")); this.settingsElement(settings); this.environmentsElement(root.evalNode("environments")); this.databaseIdProviderElement(root.evalNode("databaseIdProvider")); this.typeHandlerElement(root.evalNode("typeHandlers")); this.mapperElement(root.evalNode("mappers")); } catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3); }
}
在XMLConfigBuilder#parseConfiguration()方法里面,会对主配置文件里的所有标签进行解析
从上述代码中可以看到,XMLConfigBuilder会依次解析配置文件中的、< settings >、、< typeAliases >、< plugins >、< mappers >等属性。下面介绍下几个重要属性的解析过程。
以节点解析过程为例:
节点的定义方式有四种:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator(); while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next(); String resource; if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name"); this.configuration.addMappers(resource); } else {
resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); XMLMapperBuilder mapperParser; InputStream inputStream; if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource); inputStream = Resources.getResourceAsStream(resource); mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url); inputStream = Resources.getUrlAsStream(url); mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments()); mapperParser.parse(); } else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); }
Class> mapperInterface = Resources.classForName(mapperClass); this.configuration.addMapper(mapperInterface); }
}
}return; }
}
}
MyBatis会遍历下所有的子节点,如果当前遍历到的节点是,则MyBatis会将该包下的所有Mapper Class注册到configuration的mapperRegistry容器中。
如果当前节点为,则会依次获取resource、url、class属性,解析映射文件,并将映射文件对应的Mapper Class注册到configuration的mapperRegistry容器中。
其中,节点的解析过程如下
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());mapperParser.parse();
看构造器:
将configuration赋给BaseBuilder
创建MapperBuilderAssistant对象(该对象为MapperBuilder的协助者)
private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map, XNode> sqlFragments) {
super(configuration); this.builderAssistant = new MapperBuilderAssistant(configuration, resource); this.parser = parser; this.sqlFragments = sqlFragments; this.resource = resource;}
当有了XMLMapperBuilder后,便可进入解析的过程
public void parse() {
// 若当前的Mapper.xml尚未被解析,则开始解析 // PS:若节点下有相同的节点,那么就无需再次解析了
if (!this.configuration.isResourceLoaded(this.resource)) {
// 解析节点
this.configurationElement(this.parser.evalNode("/mapper"));
// 将该Mapper.xml添加至configuration的LoadedResource容器中,下回无需再解析
this.configuration.addLoadedResource(this.resource);
// 将该Mapper.xml对应的Mapper Class注册进configuration的mapperRegistry容器中
this.bindMapperForNamespace(); }this.parsePendingResultMaps(); this.parsePendingCacheRefs(); this.parsePendingStatements();}
解析前,会先判断Configuration是否已经加载这个XML资源,如果不存在,则调用configurationElement方法,在方法里解析所有的标签
XMLMapperBuilder#configuration
configurationElement函数
private void configurationElement(XNode context) {
try {
// 获取节点上的namespace属性,该属性必须存在,表示当前映射文件对应的Mapper Class是谁
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
// 将namespace属性值赋给builderAssistant
this.builderAssistant.setCurrentNamespace(namespace);
// 解析节点
this.cacheRefElement(context.evalNode("cache-ref"));
// 解析节点
this.cacheElement(context.evalNode("cache"));
// 解析节点
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 解析节点
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析节点
this.sqlElement(context.evalNodes("/mapper/sql"));
// 解析sql语句
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } else { throw new BuilderException("Mapper's namespace cannot be empty"); }
} catch (Exception var3) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3); }
}
此时,我们重点分析怎么去解析select | insert | update | delete
XMLMapperBuilder#buildStatementFromContext()
在这个方法里面,会调用重载的 buildStatementFromContext 方法;但是这里还不是真正解析的地方,而是遍历所有标签,然后创建一个 XMLStatementBuilder 对象,对标签进行解析。代码如下:
private void buildStatementFromContext(List list, String requiredDatabaseId) {
Iterator var3 = list.iterator(); while(var3.hasNext()) {
XNode context = (XNode)var3.next(); XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId); try {
statementParser.parseStatementNode(); } catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser); }
}
}
XMLStatementBuilder 中关于 的解析过程
XMLStatementBuilder#parseStatementNode()
public void parseStatementNode() {
String id = this.context.getStringAttribute("id"); String databaseId = this.context.getStringAttribute("databaseId"); if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
String nodeName = this.context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = this.context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false); XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant); includeParser.applyIncludes(this.context.getNode()); String parameterType = this.context.getStringAttribute("parameterType");
Class> parameterTypeClass = this.resolveClass(parameterType);
// 获取LanguateDrivate对象
String lang = this.context.getStringAttribute("lang"); LanguageDriver langDriver = this.getLanguageDriver(lang); this.processSelectKeyNodes(id, parameterTypeClass, langDriver); String keyStatementId = id + "!selectKey"; keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true); Object keyGenerator; if (this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId); } else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
// 通过LanguageDriver解析SQL内容,生成SqlSource对象
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass); StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString())); Integer fetchSize = this.context.getIntAttribute("fetchSize"); Integer timeout = this.context.getIntAttribute("timeout"); String parameterMap = this.context.getStringAttribute("parameterMap"); String resultType = this.context.getStringAttribute("resultType"); Class> resultTypeClass = this.resolveClass(resultType); String resultMap = this.context.getStringAttribute("resultMap"); String resultSetType = this.context.getStringAttribute("resultSetType"); ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType); if (resultSetTypeEnum == null) {
resultSetTypeEnum = this.configuration.getDefaultResultSetType(); }
String keyProperty = this.context.getStringAttribute("keyProperty"); String keyColumn = this.context.getStringAttribute("keyColumn"); String resultSets = this.context.getStringAttribute("resultSets"); this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
}
从上面的代码可得,首先会解析标签里的所有属性;然后创建 LanguageDriver 来解析标签里面的 SQL 配置,并生成对应的 SqlSource 对象;最后,利用工具类 MapperBuilderAssistant 来将上面解析的内容组装成MappedStatement 对象,并且注册到 Configuration 中。
上面说到,解析的SQL内容会生成对应的SqlSource对象,代码如下:
public interface SqlSource {
BoundSql getBoundSql(Object var1);}
SqlSource接口的定义非常简单,只有一个getBoundSql()方法,返回一个BoundSql实例。所以说BoundSql才是对SQL语句及参数的封装,它是SqlSource解析后的结果。
public class BoundSql {
private final String sql; private final List parameterMappings; private final Object parameterObject; private final Map, Object> additionalParameters; private final MetaObject metaParameters; public BoundSql(Configuration configuration, String sql, List parameterMappings, Object parameterObject) {
this.sql = sql; this.parameterMappings = parameterMappings; this.parameterObject = parameterObject; this.additionalParameters = new HashMap(); this.metaParameters = configuration.newMetaObject(this.additionalParameters); }……
}
解析SQL配置后,我们需要回到XMLStatementBuilder#parseStatementNode()方法中,需要研究的是最后的MapperBuilderAssistant#addMappedStatement()方法。
这个方法会根据上面 SQL 配置解析后的所有信息,封装成对应的 MappedStatement 对象,然后注册到Configuration 的 mappedStatements 属性中。mappedStatements 为 Map 结构,其中 Key 为 MapperId,Value 为 MappedStatement 对象。
创建SqlSessionFactory对象
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5; try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14); } finally {
ErrorContext.instance().reset(); try {
inputStream.close(); } catch (IOException var13) {
}
}return var5;}
解析完成之后parser.parse()函数会返回一个包含了映射文件解析结果的configuration对象,紧接着,这个对象将作为参数传递给另一个build函数,如下:
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);}
此时,SqlSessionFactory创建完毕!
SqlSession源码:
/* * execType 执行器类型 默认传入了ExecutorType.SIMPLE; * level 事务的隔离级别 默认传null * autoCommit 事务是否自动提交 默认为false * **/
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
//获取到配置中设置的当前环境 里面包含了环境id 数据库信息 和事务信息 //我们在构建的时候已经构建了一个environment
Environment environment = this.configuration.getEnvironment();
//根据环境获取其事务级别,如果环境为空或者其事务级别为空则返回默认的ManagedTransactionFactory, //我们使用SPring集成 所以会使用Spring的事务管理器 SpringManagedTransactionFactory
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
//根据数据源,事务隔离级别,是否自动提交生成事务管理器
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//根据事务和执行器类别生成执行器 //由于二级缓存默认打开 所以这儿的SimpleExecutor会被包装为CacheExecutor
Executor executor = this.configuration.newExecutor(tx, execType); var8 = new DefaultSqlSession(this.configuration, executor, autoCommit); } catch (Exception var12) { this.closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12); } finally {
ErrorContext.instance().reset(); }return var8;}
该方法的重要操作有两步,第一步是根据我们设置的Environment获取到对应的事务,第二步是根据事务和executor的type来构造一个Executor。两步的代码我们都看一下。
第一步 获取事务工厂的方法中可以看到如果我们没设置Environment,或者Environment中没有事务工厂,则会产生一个默认的
private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
return (TransactionFactory)(environment != null && environment.getTransactionFactory() != null ? environment.getTransactionFactory() : new ManagedTransactionFactory());}
第二步 构造Executor,这儿即判断是否为空,然后选择对应的处理器,这儿选择普通执行器,并且包装一下为CacheExecutor,最后记得这儿会执行一次所有拦截器的plugin方法,可以在拦截器中对Executor做自己的处理
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//如果为null则设置为默认的type 即ExecutorType.SIMPLE
executorType = executorType == null ? this.defaultExecutorType : executorType;
//如果默认的还是为null(config中默认的type被修改)则手动再设置为ExecutorType.SIMPLE
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Object 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 (this.cacheEnabled) {
//进行一次包装 为CachingExecutor
executor = new CachingExecutor((Executor)executor);
}
//注意这里会执行一次所有拦截器的plugin方法
Executor executor = (Executor)this.interceptorChain.pluginAll(executor); return executor;}
两步操作完成后我们获取到了需要的Executor,并通过传入有参构造,构建出默认的DefaultSqlSession并返回
getMapper源码
DefaultSqlSession#getMapper
public T getMapper(Class type) {
return this.configuration.getMapper(type, this);}
mapperRegistry.getMapper是从MapperRegistry的knownMappers里面取的,knownMappers里面存的是接口类型(interface mapper.UserMapper)和工厂类(MapperProxyFactory)。
Configuration#getMapper
public T getMapper(Class type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);}
从knownMappers的Map里根据接口类型(interface mapper.UserMapper)取出对应的工厂类
public T getMapper(Class type, SqlSession sqlSession) {
MapperProxyFactory mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type); if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } else {
try {
return mapperProxyFactory.newInstance(sqlSession); } catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5); }
}
}
MapperProxyFactory#newInstance
public T newInstance(SqlSession sqlSession) {
MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache); return this.newInstance(mapperProxy);}
通过JDK动态代理返回代理对象MapperProxy
protected T newInstance(MapperProxy mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{
this.mapperInterface}, mapperProxy);}
mybatis执行SQL语句源码分析:
获取到DefaultSqlSession,那么sql怎么执行呢,我们回到MapperProxy.invoke方法:
MapperProxy#invoke():
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession); } catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5); }
}
首先判断方式是不是Object类里面的方法(如:toString,hashCode),如果是Object类里面的方法就直接执行。
如果是接口里面的方法,会包装成MapperMethod,执行execute方法:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result; Object param; switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.insert(this.command.getName(), param)); break; case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.update(this.command.getName(), param)); break; case DELETE:
param = this.method.convertArgsToSqlCommandParam(args); result = this.rowCountResult(sqlSession.delete(this.command.getName(), param)); break; case SELECT:if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args); result = null; } else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args); } else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args); } else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args); } else {
param = this.method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(this.command.getName(), param); if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result); }
}break; case FLUSH:
result = sqlSession.flushStatements(); break; default:throw new BindingException("Unknown execution method for: " + this.command.getName()); }if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ")."); } else {
return result; }
}
获得SQL语句的类型,比如说select:
param = this.method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(this.command.getName(), param);
把传进来的args参数转换成我们能用的参数
public Object convertArgsToSqlCommandParam(Object[] args) {
return this.paramNameResolver.getNamedParams(args);}
getNamedParams(Object[] args)方法中,如果参数是一个直接返回,如果参数多个,封装成Map返回
public Object getNamedParams(Object[] args) {
int paramCount = this.names.size(); if (args != null && paramCount != 0) {
if (!this.hasParamAnnotation && paramCount == 1) {
return args[(Integer)this.names.firstKey()]; } else {
Map, Object> param = new ParamMap(); int i = 0; for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
Entry, String> entry = (Entry)var5.next(); param.put((String)entry.getValue(), args[(Integer)entry.getKey()]); String genericParamName = "param" + (i + 1); if (!this.names.containsValue(genericParamName)) {
param.put(genericParamName, args[(Integer)entry.getKey()]); }
}return param; }
} else {
return null; }
}
再执行DefaultSqlSession#selectOne(statement,parameter):
public T selectOne(String statement, Object parameter) {
List list = this.selectList(statement, parameter); if (list.size() == 1) {
return list.get(0); } else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else {
return null; }
}
DefaultSqlSession#selectList(statement,parameter):
public List selectList(String statement, Object parameter, RowBounds rowBounds) {
List var5; try {
MappedStatement ms = this.configuration.getMappedStatement(statement); var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception var9) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + var9, var9); } finally {
ErrorContext.instance().reset(); }return var5;}
从configuration中获得MappedStatement对象,作为参数传给Executor.query()方法
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject); CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql); return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}
获得绑定的SQL
BoundSql boundSql = ms.getBoundSql(parameterObject);
获得的boundSql包含sql的全部信息。然后再创建一个缓存的key,再调用query方法
public List query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
Cache cache = ms.getCache(); if (cache != null) {
this.flushCacheIfRequired(ms); if (ms.isUseCache() && resultHandler == null) {
this.ensureNoOutParams(ms, boundSql); List list = (List)this.tcm.getObject(cache, key); if (list == null) {
list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); this.tcm.putObject(cache, key, list); }return list; }
}return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}
1、从MappedStatement里面回去缓存,判断
2、调用delegate.query()方法
public List 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()); if (this.closed) {
throw new ExecutorException("Executor was closed."); } else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache(); }
List list; try {
++this.queryStack; list = resultHandler == null ? (List)this.localCache.getObject(key) : null; if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else {
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); }
} finally {
--this.queryStack; }if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator(); while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next(); deferredLoad.load(); }this.deferredLoads.clear(); if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache(); }
}return list; }
}
1、从以及缓存里面获取数据,判断是否有数据,如果有数据,直接从缓存中间去获取数据
2、如果缓存中没有数据,就调用queryFromDatabase()方法
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER); List list; try {
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally {
this.localCache.removeObject(key); }this.localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter); }return list;}
调用SimpleExecutor的doQuery方法
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
// 原生的JDBC的Statement对象
Statement stmt = null;
List var9;
try {
// 获得详细的配置信息
Configuration configuration = ms.getConfiguration();
// 创建StatementHandler对象,可以创建出Statement对象
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = this.prepareStatement(handler, ms.getStatementLog()); var9 = handler.query(stmt, resultHandler); } finally { this.closeStatement(stmt); }return var9;}
好了,到这里,会继续交给StatementHandler去执行。StatementHandler有2个实现类,BaseStatementHandler和RoutingStatementHandler,而RoutingStatementHandler又有3个实现类,分别是CallableStatementHandler、PreparedStatementHandler、SimpleStatementHandler。
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch(ms.getStatementType()) {
case STATEMENT:this.delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED:this.delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE:this.delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default:throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); }
}
再来看看prepareStatement(handler, ms.getStatementLog())方法,如下
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Connection connection = this.getConnection(statementLog); Statement stmt = handler.prepare(connection, this.transaction.getTimeout()); handler.parameterize(stmt); return stmt;}
再看handler.prepare是如何获取Statement的,调用的是BaseStatementHandler.prepare,如下
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(this.boundSql.getSql()); Statement statement = null; try {
statement = this.instantiateStatement(connection); this.setStatementTimeout(statement, transactionTimeout); this.setFetchSize(statement); return statement; } catch (SQLException var5) {
this.closeStatement(statement); throw var5; } catch (Exception var6) {
this.closeStatement(statement); throw new ExecutorException("Error preparing statement. Cause: " + var6, var6); }
}
instantiateStatement(connection)
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = this.boundSql.getSql(); if (this.mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = this.mappedStatement.getKeyColumns(); return keyColumnNames == null ? connection.prepareStatement(sql, 1) : connection.prepareStatement(sql, keyColumnNames); } else {
return this.mappedStatement.getResultSetType() == ResultSetType.DEFAULT ? connection.prepareStatement(sql) : connection.prepareStatement(sql, this.mappedStatement.getResultSetType().getValue(), 1007); }
}
最终是调用JDBC的connection.prepareStatement方法绑定执行sql并得到Statement。
再回来看handler.parameterize(stmt);主要是把入参绑定到Statement里,如下
public void parameterize(Statement statement) throws SQLException {
this.parameterHandler.setParameters((PreparedStatement)statement);}
在这里其实是交给ParameterHandler的唯一实现类DefaultParameterHandler来做绑定参数的工作,DefaultParameterHandler.setParameters方法如下
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId()); List 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 (SQLException | TypeException var10) {
throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10); }
}
}
}
}
在这里,通过boundSql获取到参数集合,然后循环集合,获取到TypeHandler,在通过对应的TypeHandler绑定入参。
回到SimpleExecutor.doQuery,看一下StatementHandler.query,看实现类PreparedStatementHandler.query,如下
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement; ps.execute(); return this.resultSetHandler.handleResultSets(ps);}
这里直接就执行了!!!
然后由ResultHandler.handleResultSets处理执行结果,调用的是DefaultResultSetHandler