Framework-Mybatis source code step by step (2)

Introduction

In the previous chapter, we probably learned about the initialization process of MyBatis. This chapter mainly understands SqlSessionFactoryBuilder and Configuration. It is the main tool for building SqlSessionFactory. All MyBatis configuration information can be found in Configuration. The main function of SqlSessionFactoryBuilder is to build Configuration, and then use Configuration to build SqlSessionFactory.

SqlSessionFactoryBuilder class

public class SqlSessionFactoryBuilder

It does not inherit any classes, nor does it implement any interfaces. There are only some build methods inside.

SqlSessionFactoryBuilder method
Polymorphic methods are omitted here, and all methods are finally initialized by calling the build(Configuration config) method

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {    try {        // 加载mybatis.xml文件、校验,初始化Configuration        XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);        // parser.parse()解析并加载mapper、解析mapper、构建代理类(没有初始化)        return build(parser.parse());    } catch (Exception e) {        throw ExceptionFactory.wrapException("Error building SqlSession.", e);    } finally {        ErrorContext.instance().reset();        try {            reader.close();        } catch (IOException e) {}    }}

There are 4 ways to use Reader for initial testing, the other three are polymorphic methods are omitted, Reader must not be empty

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {    try {        // 加载mybatis.xml文件、校验,初始化Configuration        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);        // parser.parse()解析并加载mapper、解析mapper、构建代理类(没有初始化)        return build(parser.parse());    } catch (Exception e) {        throw ExceptionFactory.wrapException("Error building SqlSession.", e);    } finally {        ErrorContext.instance().reset();        try {            inputStream.close();        } catch (IOException e) {}    }}

There are 4 methods to use InputStream for the initial trial, the other three are polymorphic methods are omitted, InputStream must not be empty

public SqlSessionFactory build(Configuration config) {    return new DefaultSqlSessionFactory(config);}

Whether it is through Reader or InputStream, build (Configuration config) is called to initialize

It can be seen here that the structure of SqlSessionFactoryBuilder is still very simple, as for the detailed XMLConfigBuilder in the next chapter.

Configuration class

public class Configuration

Configuration mainly stores configuration information, all mybatis.xml and mapper.xml will be in it after parsing, even mapper proxy factory, sql cache, jdbc and java type comparison table, etc. You will understand after reading too many attributes.

Configuration properties

// 环境(dev、test、gray、prod等自己配置)protected Environment environment;// 允许在嵌套语句中使用分页(RowBounds)protected boolean safeRowBoundsEnabled;// 允许在嵌套语句中使用分页(ResultHandler)protected boolean safeResultHandlerEnabled = true;// 是否开启自动驼峰命名规则映射protected boolean mapUnderscoreToCamelCase;// 和lazyLoadingEnabled配合使用。lazyLoadingEnabled为true时,// aggressiveLazyLoading为true则加载所有懒加载对象,// 为false则按需加载懒加载对象protected boolean aggressiveLazyLoading;// 延时加载protected boolean lazyLoadingEnabled = false;// 是否允许返回多个结果集protected boolean multipleResultSetsEnabled = true;// 是否允许JDBC 生成主键protected boolean useGeneratedKeys;// 使用列标签代替列名protected boolean useColumnLabel = true;// 是否开启全局二级缓存,如果配置为false,// 会覆盖各个Mapper文件中的cache配置protected boolean cacheEnabled = true;// 指定当结果集中值为 null 的时候是否调用映射对象的 // setter(map 对象时为 put)方法,这对于有 // Map.keySet() 依赖或 null 值初始化的时候是有用的。// 注意原始类型(int、boolean等)是不能设置成 null 的。protected boolean callSettersOnNulls;// 允许SQL语句中使用mapper接口声明的变量名称,前提// 是你的项目是用Java编译的,并且在mapper接口的方法// 上使用@paramprotected boolean useActualParamName = true;// 当结果集为NULL的时候返回null。设置为true,返回空属性实例protected boolean returnInstanceForEmptyRow;// 指定 MyBatis 增加到日志名称的前缀protected String logPrefix;// 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。protected Class<? extends Log> logImpl;// 指定VFS的实现类protected Class<? extends VFS> vfsImpl;// 一级缓存,mybatis利用本地缓存来防止重复查询、加速// 重复的内嵌查询。默认的作用域是Session,一次回话中的// 所有查询都会被缓存。当作用域是STATEMENT时,本地缓// 存仅作用域语句执行上,即便是同一会话的不同的调用,// 也不会有任何数据会共享protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;// 没有指定jdbcType的参数,其返回值为NULL时,指定 jdbcType。 // 某些驱动需要指定jdbcType,其他驱动直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。protected JdbcType jdbcTypeForNull = JdbcType.OTHER;// 指定某些方法懒加载protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));// SQL执行超时时间protected Integer defaultStatementTimeout;// 设置最大的抓取数量,会被query方法上设置的覆盖protected Integer defaultFetchSize;// 默认滚动策略protected ResultSetType defaultResultSetType;// 配置默认的执行器。SIMPLE执行器没有什么特别之处。// REUSE执行器重用预处理语句。BATCH执行器重用语句和批量更新protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;// 指定MyBatis如何自动映射列到字段/属性。PARTIAL只会自动映射简单,// 没有嵌套的结果。FULL会自动映射任意复杂的结果(嵌套的或其他情况)protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;// 当检测出未知列(或未知属性)时,如何处理,默认情况下没有任何提示,// 这在测试的时候很不方便,不容易找到错误。 NONE : 不做任何处理 // (默认值) WARNING : 警告日志形式的详细信息 FAILING : 映射失败,抛出异常和详细信息protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;// settings下的properties属性protected Properties variables = new Properties();// 反射工厂(MyBatis 反射的核心类Reflector)protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();// 对象工厂, 所有的类resultMap类都需要依赖于对象工厂来实例化protected ObjectFactory objectFactory = new DefaultObjectFactory();// MyBatis 提供在构造对象的时候,对于指定的对象进行特殊的加工protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();// 代理工厂,跟CglibProxyFactory一样protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // 数据源IDprotected String databaseId;// 指定一个提供Configuration实例的类. 这个被返回的Configuration实例是// 用来加载被反序列化对象的懒加载属性值. 这个类必须包含一个签名方法// static Configuration getConfiguration(). (从 3.2.3 版本开始)protected Class<?> configurationFactory;// Mapper注册器protected final MapperRegistry mapperRegistry = new MapperRegistry(this);// 拦截器protected final InterceptorChain interceptorChain = new InterceptorChain();// typeHandler注册器protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();// 类型别名注册器protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();// 方言驱动注册器protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();// 所有mapper节点集合,一个MappedStatement对应一个mapper所有节点protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")        .conflictMessageProducer((savedValue, targetValue) ->                ". please check " + savedValue.getResource() + " and " + targetValue.getResource());// 二级缓存,又叫自定义缓存,实现了Cache接口的类都可以作为二级缓存,// 所以可配置如encache等的第三方缓存。二级缓存以namespace名称空间为其唯一标识protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");// 所有resultMapprotected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");// 参数集合protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");// 主键生成策略集合protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");// 加载地址protected final Set<String> loadedResources = new HashSet<>();// sql节点protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();// 映射包含缓存-引用关系protected final Map<String, String> cacheRefMap = new HashMap<>();

There are quite a lot of Configuration properties, so it's right to not dare to pack a ticket. [Cover face][cover face][cover face]

Configuration constructor

public Configuration(Environment environment) {    // 调用无参构造函数    this();    // 初始化环境    this.environment = environment;}public Configuration() {    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);    typeAliasRegistry.registerAlias("LRU", LruCache.class);    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);    languageRegistry.register(RawLanguageDriver.class);}

There are many type aliases built in here

Important methods

public void addMappers(String packageName, Class<?> superType) {    mapperRegistry.addMappers(packageName, superType);}public void addMappers(String packageName) {    mapperRegistry.addMappers(packageName);}public <T> void addMapper(Class<T> type) {    mapperRegistry.addMapper(type);}public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    return mapperRegistry.getMapper(type, sqlSession);}

Adding and reading of Mapper will be used later in mapper parsing

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;}

Simple actuator, the realization of simple actuator is very simple, we will not go into details.


REUSE executor, REUSE and SIMPLE have a difference in doUpdate/doQuery. It is no longer closed every time a statement is executed. Instead, it is cached and reused based on the SQL text as much as possible. However, because the database server usually uses each The number of connection and global statements (called cursors in Oracle) is limited. Oracle is controlled by the open_cursors parameter, and mysql is controlled by the mysql_stmt_close parameter. This will lead to if SQL is spliced ​​out by various ifs. The database resources are exhausted. Whether it has sufficient value depends on the proportion of resources consumed by the creation of Statement statements to the overall resources and the total number of completely different Statements. Generally speaking, pure OLTP and non-automatically generated sqlmap will perform better than SIMPLE. The device is better.

The BATCH executor, the batch executor is the implementation of JDBC Statement.addBatch. For batch inserts, such as ETL that imports a large amount of data, the driver, if supported, can greatly improve the performance of DML statements (first and most importantly, network interaction It is greatly reduced), for example, for mysql, after the driver version 5.1.13 or above, the rewriteBatchedStatements parameter on the connection string is jdbc:mysql://192.168.1.100:3306/test?rewriteBatchedStatements=true, Performance can be improved dozens of times