Mybatis源码系列文章
Mybatis源码解析(四):sql语句及#{}、${}的解析
Mybatis源码解析(五):SqlSession会话的创建
在环境搭建章节,通过传统方式测试如下:
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
User user = sqlSession.selectOne("user.findUserById", 100);
System.out.println(user);
sqlSession.close();
}
第一步操作:Resources.getResourceAsStream(“sqlMapConfig.xml”)
public class Dept {
private Integer deptno;
private String dname;
//步骤5:实体类提供全参构造方法
private Dept(Integer deptno, String dname) {
this.deptno = deptno;
this.dname = dname;
}
public static DeptBuilder newBuilder() { //步骤 2:提供返回Builder的静态方法
return new DeptBuilder();
}
public static class DeptBuilder { //步骤 1:定义Builder类
//步骤3:提供和实体类一样的属性
private Integer deptno;
private String dname;
//步骤4:提供设置属性的方法
public DeptBuilder deptno(Integer deptno) {
this.deptno = deptno;
return this;
}
public DeptBuilder dname(String dname) {
this.dname = dname;
return this;
}
public Dept build(){ //步骤6:提供返回实体类的方法
return new Dept(deptno,dname);
}
}
}
第二步操作:new SqlSessionFactoryBuilder().build(resourceAsStream)
build方法实现类
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// XMLConfigBuilder:用来解析XML配置文件
// 使用构建者模式:好处:降低耦合、分离复杂对象的创建
// 1.创建XPathParser解析器对象,根据inputStream解析成了document对象 2.创建全局配置对象Configuration对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// parser.parse():使用XPATH解析XML配置文件,将配置文件封装到Configuration对象
// 返回DefaultSqlSessionFactory对象,该对象拥有Configuration对象(封装配置文件信息)
// parse():配置文件就解析完成了
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.
}
}
}
XMLConfigBuilder构造方法
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
// XPathParser基于 Java XPath 解析器,用于解析 MyBatis中的配置文件
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 创建Configuration对象,并通过TypeAliasRegistry注册一些Mybatis内部相关类的别名
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
Configuration对象
核心配置文件xml可以配置的标签如下
核心配置文件将会被解析为Configuration配置类,而里面的标签则会被解析为对应的属性
environment中包含statementId:“namespace.id”、事务工厂和数据源信息
public class Configuration {
protected Environment environment;
// 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。默认为false
protected boolean safeRowBoundsEnabled;
// 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false
protected boolean safeResultHandlerEnabled = true;
// 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN
// 到经典 Java 属性名 aColumn 的类似映射。默认false
protected boolean mapUnderscoreToCamelCase;
// 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。默认值false (true in ≤3.4.1)
protected boolean aggressiveLazyLoading;
// 是否允许单一语句返回多结果集(需要兼容驱动)。
protected boolean multipleResultSetsEnabled = true;
// 允许 JDBC 支持自动生成主键,需要驱动兼容。这就是insert时获取mysql自增主键/oracle sequence的开关。
// 注:一般来说,这是希望的结果,应该默认值为true比较合适。
protected boolean useGeneratedKeys;
// 使用列标签代替列名,一般来说,这是希望的结果
protected boolean useColumnLabel = true;
// 是否启用缓存
protected boolean cacheEnabled = true;
// 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,
// 这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。
protected boolean callSettersOnNulls;
// 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,
// 并且加上-parameters选项。(从3.4.1开始)
protected boolean useActualParamName = true;
//当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。
// 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始)
// 注:这里应该拆分为两个参数比较合适, 一个用于结果集,一个用于单记录。
// 通常来说,我们会希望结果集不是null,单记录仍然是null
protected boolean returnInstanceForEmptyRow;
protected boolean shrinkWhitespacesInSql;
// 指定 MyBatis 增加到日志名称的前缀。
protected String logPrefix;
// 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。一般建议指定为slf4j或log4j
protected Class<? extends Log> logImpl;
// 指定VFS的实现, VFS是mybatis提供的用于访问AS内资源的一个简便接口
protected Class<? extends VFS> vfsImpl;
protected Class<?> defaultSqlProviderType;
// MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
// 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
// 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
// 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,
// 多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
// 指定对象的哪个方法触发一次延迟加载。
protected Set<String> lazyLoadTriggerMethods
= new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
// 设置超时时间,它决定驱动等待数据库响应的秒数。默认不超时
protected Integer defaultStatementTimeout;
// 为驱动的结果集设置默认获取数量。
protected Integer defaultFetchSize;
// SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements);
// BATCH 执行器将重用语句并执行批量更新。
protected ResultSetType defaultResultSetType;
// 默认执行器类型
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
// 指定 MyBatis 应如何自动映射列到字段或属性。
// NONE 表示取消自动映射;
// PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。
// FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
// 指定发现自动映射目标未知列(或者未知属性类型)的行为。这个值应该设置为WARNING比较合适
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior
= AutoMappingUnknownColumnBehavior.NONE;
// settings下的properties属性
protected Properties variables = new Properties();
// 默认的反射器工厂,用于操作属性、构造器方便
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
// 对象工厂, 所有的类resultMap类都需要依赖于对象工厂来实例化
protected ObjectFactory objectFactory = new DefaultObjectFactory();
// 对象包装器工厂,主要用来在创建非原生对象,比如增加了某些监控或者特殊属性的代理类
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
// 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
protected boolean lazyLoadingEnabled = false;
// 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。MyBatis 3.3+使用JAVASSIST
protected ProxyFactory proxyFactory = new JavassistProxyFactory();
// MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
*
*/
protected Class<?> configurationFactory;
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
// mybatis插件列表
protected final InterceptorChain interceptorChain = new InterceptorChain();
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry(this);
// 类型注册器, 用于在执行sql语句的出入参映射以及mybatis-config文件里的各种配置
// 比如时使用简写
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
protected final Map<String, MappedStatement> mappedStatements =
new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
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<>();
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<>();
/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<>();
public Configuration(Environment environment) {
this();
this.environment = environment;
}
...
}
Configuration构造函数
public Configuration() {
/**
* 注册事务工厂的别名
*/
// 利用java.sql.Connection对象完成对事务的提交
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
// mybatis自身不会去实现事务管理,而是让程序的容器(JBOSS,WebLogic)来实现对事务的管理
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
/**
* 注册数据源的别名
*/
// mybatis会从在应用服务器向配置好的JNDI数据源DataSource获取数据库连接。在生产环境中优先考虑这种方式
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
// mybatis会创建一个数据库连接池,连接池的一个连接将会被用作数据库操作。一旦数据库操作完成,
// mybatis会将此连接返回给连接池。在开发或测试环境中经常用到此方式
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
// mybatis会为每一个数据库操作创建一个新的连接,并关闭它。该方式适用于只有小规模数量并发用户的简单应用程序上
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);
}
JDBC事务工厂类
public class JdbcTransactionFactory implements TransactionFactory {
@Override
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
构造函数:连接、隔离级别、是否自动提交
public class JdbcTransaction implements Transaction {
private static final Log log = LogFactory.getLog(JdbcTransaction.class);
protected Connection connection;
protected DataSource dataSource;
protected TransactionIsolationLevel level;
protected boolean autoCommit;
public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
dataSource = ds;
level = desiredLevel;
autoCommit = desiredAutoCommit;
}
...
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// XMLConfigBuilder:用来解析XML配置文件
// 使用构建者模式:好处:降低耦合、分离复杂对象的创建
// 1.创建XPathParser解析器对象,根据inputStream解析成了document对象 2.创建全局配置对象Configuration对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// parser.parse():使用XPATH解析XML配置文件,将配置文件封装到Configuration对象
// 返回DefaultSqlSessionFactory对象,该对象拥有Configuration对象(封装配置文件信息)
// parse():配置文件就解析完成了
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.
}
}
}
进入XMLConfigBuilder类的parse()解析方法中
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// parser.evalNode("/configuration"):通过XPATH解析器,解析configuration根节点
// 从configuration根节点开始解析,最终将解析出的内容封装到Configuration对象中
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
进parseConfiguration方法解析XNode对象
private void parseConfiguration(XNode root) {
try {
// 解析标签
propertiesElement(root.evalNode("properties"));
// 解析标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
// 解析标签
typeAliasesElement(root.evalNode("typeAliases"));
// 解析标签
pluginElement(root.evalNode("plugins"));
// 解析标签
objectFactoryElement(root.evalNode("objectFactory"));
// 解析标签
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析标签
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 解析标签
environmentsElement(root.evalNode("environments"));
// 解析标签
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析标签
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析标签 加载映射文件流程主入口
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
进入environmentsElement(root.evalNode(“environments”));
private void environmentsElement(XNode context) throws Exception {
// 如果定义了environments
if (context != null) {
// 方法参数如何没有传递 environment,则解析sqlMapConfig.xml中的
if (environment == null) {
//
environment = context.getStringAttribute("default");
}
//遍历解析environment节点
for (XNode child : context.getChildren()) {
//获得id属性值
String id = child.getStringAttribute("id");
//判断id和environment值是否相等
if (isSpecifiedEnvironment(id)) {
/*
创建事务工厂对象
*/
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
/*
创建数据源对象
*/
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 将Environment存到configuraion中
configuration.setEnvironment(environmentBuilder.build());
break;
}
}
}
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
//通过Class对象构造函数创建对象
TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
构建Environment对象,赋值configuration
public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
...
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
// 调用openSessionFromDataSource 参数1:执行器类型 参数2:事务隔离级别 参数三:指定事务是否自动提交
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
...
}