框架 - Mybatis 源码一步步深入(二)
简介
上一章我们大概了解了MyBatis初始化过程,本章主要了解SqlSessionFactoryBuilder、Configuration,它是构建SqlSessionFactory的主要工具,所有MyBatis配置信息都可以在Configuration中找到,SqlSessionFactoryBuilder的主要作用作用就是构建Configuration,然后使用Configuration构建SqlSessionFactory。
SqlSessionFactoryBuilder 类
public class SqlSessionFactoryBuilder
它不继承任何类,也不实现任何接口,内部只有一些build方法
SqlSessionFactoryBuilder 方法
这里省略多态方法,所有方法最终都只是调用build(Configuration config) 方法初始化
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) {} ? ? } }
使用Reader初试化的有4个方法,其他三个是多态方法被省略,Reader一定不能为空
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) {} ? ? } }
使用InputStream初试化的有4个方法,其他三个是多态方法被省略,InputStream一定不能为空
public SqlSessionFactory build(Configuration config) { ? ? return new DefaultSqlSessionFactory(config); }
无论是通过Reader还是InputStream最终都调用build(Configuration config)初始化
这里可以看出SqlSessionFactoryBuilder结构还是很简单的,至于详细的XMLConfigBuilder下一章节中主要盘它。
Configuration 类
public class Configuration
Configuration主要存放配置信息,所有mybatis.xml和mapper.xml解析后都会在这里面,甚至mapper代理工厂,sql缓存器,jdbc跟java类型对照表等,太多了看完属性你就明白了
Configuration 属性
// 环境(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接口的方法 // 上使用@param protected 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();? // 数据源ID protected 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"); // 所有resultMap protected 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<>();
Configuration 属性相当的多,不敢打包票都对。[捂脸][捂脸][捂脸]
Configuration 构造函数
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); }
这里内置了很多类型别名
Configuration重要方法
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); }
Mapper的添加和读取,后面将mapper解析会用到
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执行器,简单执行器的实现非常的简单,我们就不展开详述了。
REUSE执行器,REUSE和SIMPLE在doUpdate/doQuery上有个差别,不再是每执行一个语句就close掉了,而是尽可能的根据SQL文本进行缓存并重用,但是由于数据库服务器端通常对每个连接以及全局的语句(oracle称为游标)handler的数量有限制,oracle中是open_cursors参数控制,mysql中是mysql_stmt_close参数控制,这就会导致如果sql都是靠if各种拼接出来,日积月累可能会导致数据库资源耗尽。其是否有足够价值,视创建Statement语句消耗的资源占整体资源的比例、以及一共有多少完全不同的Statement数量而定,一般来说,纯粹的OLTP且非自动生成的sqlmap,它会比SIMPLE执行器更好。
BATCH执行器,批量执行器是JDBC Statement.addBatch的实现,对于批量insert而言比如导入大量数据的ETL,驱动器如果支持的话,能够大幅度的提高DML语句的性能(首先最重要的是,网络交互就大幅度减少了),比如对于mysql而言,在5.1.13以上版本的驱动,在连接字符串上rewriteBatchedStatements参数也就是jdbc:mysql://localhost:3306/testdb?rewriteBatchedStatements=true后,性能可以提高几十倍