1、通过 SqlSessionFactoryBuilder 类中的静态方法 builder 获取 SqlSessionFactory 对象
public SqlSessionFactory getSqlSessionFactory() throws IOException {
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过 SqlSessionFactoryBuilder 类中的方法 builder 获取 SqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
return sqlSessionFactory;
}
@Test
public void test01() throws IOException {
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession();
try{
EmployeeCacheMapper mapper = sqlSession.getMapper(EmployeeCacheMapper.class);
Employee emp01 = mapper.getEmpById(1);
System.out.println(emp01);
Employee emp02 = mapper.getEmpById(1);
System.out.println(emp02);
System.out.println(emp01 == emp02);
} finally {
sqlSession.close();
}
}
2、SqlSessionFactoryBuilder 类中的 builder 方法
builder 方法主要做了三件事:
- 创建 XMLConfigBuilder 对象,该对象主要用于解析 Mybatis 的全局配置文件以及 Mapper 映射文件
- 调用 XMLConfigBuilder 对象的 parse() 方法,对传入的配置文件进行解析
- 调用当前 SqlSessionFactoryBuilder 对象的 build 方法,创建 SqlSession 对象,并返回
package org.apache.ibatis.session;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 创建 XMLConfigBuilder 对象,主要用于解析 Mybatis 的全局配置文件以及 Mapper 映射文件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 调用 XMLConfigBuilder 对象的 parse() 方法对传入的配置文件进行解析
// 调用本类当中的 build 方法,创建 SqlSession 对象,并返回
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.
}
}
}
public SqlSessionFactory build(Configuration config) {
// DefaultSqlSessionFactory 是 SqlSession 接口的实现类
return new DefaultSqlSessionFactory(config);
}
}
3、XMLConfigBuilder 类中的 parse() 方法
XMLConfigBuilder 继承了 BaseBuilder,Configuration 变量是在 BaseBuilder 中声明的。在 XMLConfigBuilder 类中,其构造函数在初始时便调用了父类的构造函数,并传入了 Configuration 对象。
parse() 方法主要用于解析 Mybatis 全局配置文件以及 Mapper 映射文件中的内容,并将其中的相关内容封装到 Configuration 对象中
BaseBuilder.java
public abstract class BaseBuilder {
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
}
XMLConfigBuilder.java
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 调用父类中的构造函数
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
// 该方法主要用于解析 Mybatis 全局配置文件以及 Mapper 映射文件中的内容,并将其中的相关内容封装到 Configuration 对象中
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 解析 properties 标签:引入外部properties文件
propertiesElement(root.evalNode("properties"));
// 解析 settings 标签
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
// 解析 typeAliases 标签
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"));
// 解析 Mapper 映射文件
// 重点关注此方法
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
// 判断全局配置文件中是如何配置映射文件相关信息的
// 案例中使用的 package 标签,其他条件不再关注
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 如果是以 package 标签进行配置的
if ("package".equals(child.getName())) {
// 获取 package 标签中的 name 属性的值
String mapperPackage = child.getStringAttribute("name");
// 调用 Configuration 对象的 addMappers 方法
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 mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
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) {
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.");
}
}
}
}
}
}
4、Configuration 类中的 addMappers 方法
public class Configuration {
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public void addMappers(String packageName) {
// 调用 MapperRegistry 对象的 addMappers 方法
mapperRegistry.addMappers(packageName);
}
}
5、MapperRegistry 类中的 addMappers 方法
addMappers 方法创建了 Mapper 接口对应的代理对象工厂,后续会由代理对象工厂来创建代理对象,并以 Mapper 接口为 key,代理对象工厂为 value,封装进 knowMappers(Map集合)
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();
public MapperRegistry(Configuration config) {
this.config = config;
}
public <T> void addMapper(Class<T> type) {
// 判断传入的 Class 是否是接口
if (type.isInterface()) {
// 如果 knownMappers 中已经包含当前 Class
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 创建接口对应的代理对象工厂,并以 Mapper 接口为 key,代理对象工厂为 value,封装进 knowMappers(Map集合)
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
// 创建 MapperAnnotationBuilder 对象,并调用 parse() 方法
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
}
public void addMappers(String packageName, Class<?> superType) {
// 创建 ResolverUtil 工具类
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
// 根据 packageName,即 mapper 文件所在的包路径,将 mapper 文件对应的 Mapper 接口全类名存放到一个 set 集合中
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
// 获取 Mapper 接口的 set 集合
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
// 遍历 Mapper 接口,并调用 addMapper 方法,该方法主要用于创建 Mapper 接口对应的代理对象
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
/**
* @since 3.2.2
*/
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
}
}
6、MapperAnnotationBuilder 中的 parse() 方法
public class MapperAnnotationBuilder {
private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();
private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();
private final Configuration configuration;
private final MapperBuilderAssistant assistant;
private final Class<?> type;
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
public void parse() {
// 获取 Mapper 接口的全类名
String resource = type.toString();
// 判断 Configuration 对象中是否已经加载过该资源
if (!configuration.isResourceLoaded(resource)) {
// 加载 Mapper 接口对应的 xml 文件
loadXmlResource();
// 将 Mapper 接口全类名存放进名为 loadedResources 的 set 集合
configuration.addLoadedResource(resource);
// 设置当前 Mapper.xml 的 namespace
assistant.setCurrentNamespace(type.getName());
// 解析缓存
parseCache();
// 解析引用缓存
parseCacheRef();
// 获取 Mapper 接口中所有的方法信息
Method[] methods = type.getMethods();
// 遍历方法
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
// 调用 parseStatement 方法
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
void parseStatement(Method method) {
// 获取方法的参数类型
Class<?> parameterTypeClass = getParameterType(method);
LanguageDriver languageDriver = getLanguageDriver(method);
// 获取 mapper.xml 文件中的 SQL 语句块信息
// sqlSource 对象包含三个变量,一是 SQL 语句,二是参数映射信息,三是 Configuration 对象
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
Options options = method.getAnnotation(Options.class);
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
// 获取 SQL 命令类型(select、update、insert、delete)
SqlCommandType sqlCommandType = getSqlCommandType(method);
// 是否是查询命令
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 是否刷新缓存
boolean flushCache = !isSelect;
// 是否使用缓存
boolean useCache = isSelect;
KeyGenerator keyGenerator;
String keyProperty = "id";
String keyColumn = null;
// 如果是insert或者update命令
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// first check for SelectKey annotation - that overrides everything else
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else if (options == null) {
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
} else {
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
} else {
keyGenerator = NoKeyGenerator.INSTANCE;
}
if (options != null) {
if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
flushCache = true;
} else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
flushCache = false;
}
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
resultSetType = options.resultSetType();
}
String resultMapId = null;
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
String[] resultMaps = resultMapAnnotation.value();
StringBuilder sb = new StringBuilder();
for (String resultMap : resultMaps) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(resultMap);
}
resultMapId = sb.toString();
} else if (isSelect) {
resultMapId = parseResultMap(method);
}
// 将 MappedStatement 封装进 Configuration 对象中
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}
}