mybatis源码_MyBatis源码篇

Mybatis源码分析

一、整体架构图

mybatis源码_MyBatis源码篇_第1张图片

二、核心组件

  • 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对象的形式存储。

mybatis源码_MyBatis源码篇_第2张图片

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创建完毕!

mybatis源码_MyBatis源码篇_第3张图片

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并返回

mybatis源码_MyBatis源码篇_第4张图片

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源码_MyBatis源码篇_第5张图片

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

mybatis源码_MyBatis源码篇_第6张图片

你可能感兴趣的:(mybatis源码)