MyBatis-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
MyBatis-SqlSessionFactory的创建的更多相关文章
- MyBatis SqlSessionFactory的几种常见创建方式
原文链接:https://blog.csdn.net/jimolangge123/article/details/49228255 MyBatis框架主要是围绕着SqlSessionFactory这个 ...
- 使用MyBatis Generator自动创建代码
SSM框架--使用MyBatis Generator自动创建代码 1. 目录说明 使用自动生成有很多方式,可以在eclipse中安装插件,但是以下将要介绍的这种方式我认为很轻松,最简单,不需要装插件, ...
- MyBatis Generator自动创建代码
MyBatis Generator自动创建代码 1.首先在eclipse上安装mybatis插件 2.创建一个mavenWeb项目. 3.在resource中写入一个xml,一定要与我得同名 < ...
- MyBatis - SqlSessionFactory 与 SqlSession
SqlSessionFactory SqlSessionFactory是创建SqlSession的工厂,一般使用单例模式,不需要重复创建. SqlSession SqlSession是直接与数据库直接 ...
- Mybatis SqlsessionFactory
在Mybatis 与 Spring 进行整合的时候,我们会进行sqlSessionFactory 的配置,来创建sqlSessionFactory 对象:如下: <bean id="s ...
- Mybatis总结一之Mybatis项目的创建
一.mybatis概念 Mybatis是对象和表之间映射关系的持久层框架. 二.Mybatis的导入与创建 第一步,创建web项目,引入mybatis依赖的jar包----mybatis-3.4.6. ...
- mybatis 原生写法创建项目
一.创建全局文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuratio ...
- MyBatis学习总结_09_使用MyBatis Generator自动创建代码
一.构建一个环境 1. 首先创建一个表: CREATE TABLE t_user ( USER_ID INT NOT NULL AUTO_INCREMENT, USER_NAME CHAR(30) N ...
- 使用Spring MVC,Mybatis框架等创建Java Web项目时各种前期准备的配置文件内容
1.pom.xml 首先,pom.xml文件,里面包含各种maven的依赖,代码如下: <project xmlns="http://maven.apache.org/POM/4.0. ...
- MyBatis学习总结(9)——使用MyBatis Generator自动创建代码
一.构建一个环境 1. 首先创建一个表: [sql] view plaincopy CREATE TABLE t_user ( USER_ID INT NOT NULL AUTO_INCREMENT, ...
随机推荐
- python 模块之-pickle
Pickle的问题和所有其他编程语言特有的序列化问题一样,就是它只能用于Python,并且可能不同版本的Python彼此都不兼容,因此,只能用Pickle保存那些不重要的数据,不能成功地反序列化也没关 ...
- 第二十二天 logging hashlib re 模块
今日内容 logging功能完善的日志模块 re正则表达式模块主要处理字符串匹配 查找 搜索给你一个字符串 要从中找到你需要的东西 爬虫大量使用 hashlib hash算法相关的库 算法怎么算不需要 ...
- POJ 3349-Snowflake Snow Snowflakes-字符串哈希
哈希后,对每片雪花对比6次. #include <cstdio> #include <cstring> #include <vector> #include < ...
- 为什么Elasticsearch查询变得这么慢了?
参考内容:https://mp.weixin.qq.com/s/RTpBaFpNELQCO6VE0KMfsw
- Codeforces986E Prince's Problem 【虚树】【可持久化线段树】【树状数组】
我很喜欢这道题. 题目大意: 给出一棵带点权树.对每个询问$ u,v,x $,求$\prod_{i \in P(u,v)}gcd(ai,x)$.其中$ P(u,v) $表示$ u $到$ v $的路径 ...
- Codeforces510 C. Fox And Names
Codeforces题号:#510C 出处: Codeforces 主要算法:判环+拓扑 难度:4.2 思路分析: 要是把这道题联系到图上就很容易想了. 如何建图?由于最后要求名字满足字典序,所以不妨 ...
- python+appium里的等待时间
为什么要用等待时间: 今天在写App的自动化的脚本时发现一个元素,时而能点击,时而又不能点击到,很是心塞,原因是:因为元素还没有被加载出来,查找的代码就已经被执行了,自然就找不到元素了.解决方式:可以 ...
- git同步远程已删除的分支和删除本地多余的分支
使用 git branch -a 可以查看本地分支和远程分支情况 但远程分支(红色部分)删除后,发现本地并没有同步过来. 一. 同步本地的远程分支 查看本地分支和追踪情况: git remote sh ...
- ftp:linux下利用shell脚本添加虚拟用户并赋予权限
首先ftp配置应为虚拟用户登录模式 用户密码文本目录为/etc/vsftpd/vftpuser,代码如下: #!/bin/bash # ];then username=$ password=$ hom ...
- 【BZOJ5339】[TJOI2018]教科书般的亵渎(斯特林数)
[BZOJ5339][TJOI2018]教科书般的亵渎(斯特林数) 题面 BZOJ 洛谷 题解 显然交亵渎的次数是\(m+1\). 那么这题的本质就是让你求\(\sum_{i=1}^n i^{m+1} ...