12MyBatis-SqlSessionFactory的创建
Posted 江湖小小白
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了12MyBatis-SqlSessionFactory的创建相关的知识,希望对你有一定的参考价值。
Main 方法,mybatis 版本为 3.5.0
解析配置文件的所有信息,保存在 Configuration 中,返回包含 Configuration 的 DefaultSqlSession
MappedStatement:代表一个增删改查的详细信息
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); session = sqlSessionFactory.openSession();
new SqlSessionFactoryBuilder().build(inputStream)
org.apache.ibatis.session.SqlSessionFactoryBuilder
/** * @param inputStream 参照 XML 文档或更特定的 SqlMapConfig.xml 文件的 InputStream 实例 * @param environment 可选的参数,决定加载哪种环境(开发环境/生产环境),包括数据源和事务管理器 * @param properties 可选的参数,使用 properties,就会加载那些 properties(属性配置文件),属性可以用 ${propName} 语法形式多次用在配置文件中。和 Spring 很像,一个思想? * @return */ public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { //委托 XMLConfigBuilder 来解析 xml 文件,并构建 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } /** * @param config Configuration * @return DefaultSqlSessionFactory */ public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
parser.parse()
org.apache.ibatis.builder.xml.XMLConfigBuilder
// 解析配置 public Configuration parse() { // 如果已经解析过了,报错 if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; // XML 配置文件根节点是 configuration parseConfiguration(parser.evalNode("/configuration")); return configuration; } // 分步骤解析 private void parseConfiguration(XNode root) { try { //issue #117 read properties first // 1.properties propertiesElement(root.evalNode("properties")); // 2.设置 Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); // 显式定义用什么log框架,不定义则用默认的自动发现 jar 包机制 loadCustomLogImpl(settings); // 3.类型别名 typeAliasesElement(root.evalNode("typeAliases")); // 4.插件 pluginElement(root.evalNode("plugins")); // 5.对象工厂 objectFactoryElement(root.evalNode("objectFactory")); // 6.对象包装工厂 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // 7.环境 environmentsElement(root.evalNode("environments")); // 8.数据库环境 databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 9.类型处理器 typeHandlerElement(root.evalNode("typeHandlers")); // 10.SQL映射器 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } // 设置 private void settingsElement(Properties props) { // 如何自动映射列到字段属性 configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); // MyBatis 自动映射时未知列或未知属性处理策略,通过该配置可指定 MyBatis 在自动映射过程中遇到未知列或者未知属性时如何处理,默认不做任何处理 configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); // 缓存 configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true)); // proxyFactory (CGLIB | JAVASSIST),延迟加载的核心技术就是用代理模式,CGLIB/JAVASSIST 两者选一 configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory"))); // 延迟加载 configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false)); // 延迟加载时,每种属性是否还要按需加载 configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false)); // 允不允许多种结果集从一个单独 的语句中返回 configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true)); // 使用列标签代替列名 configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true)); // 允许 JDBC 支持生成的键 configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false)); // 配置默认的执行器 configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE"))); // 超时时间 configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null)); // 为驱动程序设置一个提示,以控制返回结果的获取大小。可以通过查询设置覆盖此参数值 configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null)); // 是否将DB字段自动映射到驼峰式Java属性(A_COLUMN-->aColumn) configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false)); // 嵌套语句上使用RowBounds configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false)); // 默认用session级别的缓存 configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION"))); // 为 null 值设置 jdbctype configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER"))); // Object 的哪些方法将触发延迟加载 configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString")); // 使用安全的 ResultHandler configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true)); // 动态 SQL 生成语言所使用的脚本语言 configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage"))); // 默认枚举类型处理器 configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler"))); // 当结果集中含有 Null 值时是否执行映射对象的 setter 或者 Map 对象的 put 方法。此设置对于原始类型如 int,boolean 等无效 configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); // 允许使用方法签名中的名称作为语句参数名称。使用该特性,工程必须采用 Java 8 编译,并且加上-parameters选项。(从3.4.1开始) // 设置为true时传递参数需要使用 #{arg0}-#{argn}或者#{param1}-#{paramn},设置为false时 传递参数需要使用 #{0}-#{n}或者#{param1}-#{paramn} configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); // 当返回行的所有列都是空时,MyBatis默认返回null。当开启时,MyBatis 会返回一个空实例。它也适用于嵌套的结果集(从3.4.2开始) configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); // logger 名字的前缀 configuration.setLogPrefix(props.getProperty("logPrefix")); // 配置工厂 configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); } // 配置 SQL 映射器 private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { // 自动扫描包下所有映射器 String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { // 使用类路径 ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); // 映射器比较复杂,调用 XMLMapperBuilder,注意在 for 循环里每个 mapper 都重新 new 一个 XMLMapperBuilder 来解析 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { // 使用绝对 url 路径 ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { // 使用 java 类名 Class<?> mapperInterface = Resources.classForName(mapperClass); // 直接把这个映射加入配置 configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
mapperElement(root.evalNode("mappers")) ---> mapperParser.parse()
org.apache.ibatis.builder.xml.XMLMapperBuilder
// 解析 SQL 映射文件 public void parse() { // 如果没有加载过再加载,防止重复加载 if (!configuration.isResourceLoaded(resource)) { // 配置 mapper configurationElement(parser.evalNode("/mapper")); // 标记一下,已经加载过了 configuration.addLoadedResource(resource); // 绑定映射器到 namespace bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); } private void configurationElement(XNode context) { try { // 1.配置 namespace String namespace = context.getStringAttribute("namespace"); if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper\'s namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); // 2.配置 cache-ref cacheRefElement(context.evalNode("cache-ref")); // 3.配置 cache cacheElement(context.evalNode("cache")); //4.配置 parameterMap (已经废弃,老式风格的参数映射) parameterMapElement(context.evalNodes("/mapper/parameterMap")); //5.配置 resultMap (高级功能) resultMapElements(context.evalNodes("/mapper/resultMap")); //6.配置 sql (定义可重用的 SQL 代码段) sqlElement(context.evalNodes("/mapper/sql")); //7.配置 select|insert|update|delete buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is \'" + resource + "\'. Cause: " + e, e); } } // 7.配置 select|insert|update|delete private void buildStatementFromContext(List<XNode> list) { if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null); } // 7.1 构建语句 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { // 构建所有语句,一个 mapper 下可以有很多 select,语句比较复杂,核心都在这里面,所以调用 XMLStatementBuilder final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { // 核心 XMLStatementBuilder.parseStatementNode statementParser.parseStatementNode(); } catch (IncompleteElementException e) { // 如果出现SQL语句不完整,把它记下来,放到 configuration 去 configuration.addIncompleteStatement(statementParser); } } } private void bindMapperForNamespace() { // 获取 mapper 映射文件的命名空间 String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { // 创建 mapper 映射文件的代理接口,后续就可以 session.getMapper(xxxMapper.class) boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { // 将命名空间和代理接口添加至 configuration if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType); } } } }
statementParser.parseStatementNode()
org.apache.ibatis.builder.xml.XMLStatementBuilder
// 解析语句(select|insert|update|delete) public void parseStatementNode() { String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); // 如果databaseId不匹配,退出 if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } // 驱动程序每次批量返回的结果行数 Integer fetchSize = context.getIntAttribute("fetchSize"); // 超时时间 Integer timeout = context.getIntAttribute("timeout"); // 引用外部 parameterMap,已废弃 String parameterMap = context.getStringAttribute("parameterMap"); // 参数类型 String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); // 引用外部的 resultMap(高级功能) String resultMap = context.getStringAttribute("resultMap"); // 结果类型 String resultType = context.getStringAttribute("resultType"); // 脚本语言,mybatis3.2 的新功能 String lang = context.getStringAttribute("lang"); // 得到语言驱动 LanguageDriver langDriver = getLanguageDriver(lang); Class<?> resultTypeClass = resolveClass(resultType); // 结果集类型,FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE 中的一种 String resultSetType = context.getStringAttribute("resultSetType"); // 语句类型, STATEMENT|PREPARED|CALLABLE 中的一种 StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); // 获取命令类型(select|insert|update|delete) String nodeName = context.getNode().getNodeName(); SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); // 是否要缓存 select 结果 boolean useCache = context.getBooleanAttribute("useCache", isSelect); // 仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况 // 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing // 解析之前先解析<include>SQL片段 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); // Parse selectKey after includes and remove them. // 解析之前先解析<selectKey> processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) // 解析成SqlSource,一般是 DynamicSqlSource SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); String resultSets = context.getStringAttribute("resultSets"); // (仅对 insert 有用) 标记一个属性, MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值 String keyProperty = context.getStringAttribute("keyProperty"); // (仅对 insert 有用) 标记一个属性, MyBatis 会通过 getGeneratedKeys 或者通过 insert 语句的 selectKey 子元素设置它的值 String keyColumn = context.getStringAttribute("keyColumn"); KeyGenerator keyGenerator; String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } // 调用助手类,最终添加到 configuration 的 mappedStatements 中 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }
时序图
https://github.com/tuguangquan/mybatis/tree/master/src/main/java/org/apache/ibatis
以上是关于12MyBatis-SqlSessionFactory的创建的主要内容,如果未能解决你的问题,请参考以下文章
12.1 LNMP架构介绍 12.2 MySQL安装 12.3/12.4 PHP安装 12.5 Nginx介绍
12.1LNMP架构介绍12.2 MySQL安装12.3/12.4 PHP安装12.5 Nginx介绍
十二周二次课 12.6 Nginx安装 12.7 默认虚拟主机 12.8 Nginx用户认证 12.
12.1 LNMP架构介绍12.2 MySQL安装12.3/12.4 PHP安装12.5 Nginx