上篇博客已经简单介绍了Mybatis的整体架构,源码部分也会按照架构图来梳理,本节就先从Mybatis的配置文件开始。
mybatis-config.xml
平时工作时,大家用Mybatis注解多一些还是xml多一些?对于Mybatis框架而言,xml可能更加灵活、方便一些,常用且主要的xml一共有2个,一个是mybatis-config.xml,另一个就是Mapper对应的xml。所以,先从xml开始讲解。
mybatis-config.xml作为我们常用并且很主要的配置文件,主要配置mybatis全局配置,db连接等信息。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties" ></properties>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<typeAliases>
<package name="com.test.bean"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<databaseIdProvider type="DB_VENDOR">
<property name="MySQL" value="mysql"/>
<property name="SQL Server" value="sqlserver"/>
<property name="Oracle" value="oracle"/>
</databaseIdProvider>
<mappers>
<package name="com.test.dao"/>
</mappers>
</configuration>
测试类
通过读取配置文件以流的形式构建SqlSessionFactory来看底层的一个实现。
public void test02 () {
// 根据全局配置文件创建出SqlSessionFactory
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
// SqlSessionFactory:负责创建SqlSession对象的工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// SqlSession:表示跟数据库建议的一次会话
// 获取数据库的会话,创建出数据库连接的会话对象(事务工厂,事务对象,执行器,如果有插件的话会进行插件的解析)
SqlSession sqlSession = sqlSessionFactory.openSession();
Emp empByEmpno = null;
try {
// 获取要调用的接口类,创建出对应的mapper的动态代理对象(mapperRegistry.knownMapper)
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
// 调用方法开始执行
empByEmpno = mapper.findEmpByEmpnoAndEname(7369, "SMITH");
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
System.out.println(empByEmpno);
}
SqlSessionFactoryBuilder().build()
build方法中会根据解析到的xml文件,转化成XMLConfigBuilder对象,
//properties:引入额外配置
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
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.
}
}
}
XMLConfigBuilder
XPathParser中,会将xml转换成document文件,并用this方法对XMLConfigBuilder和Configuration进行初始化赋值
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
//将mybatis.xml转换成docment
this.document = createDocument(new InputSource(inputStream));
}
//调用this构造器方法,对XMLConfigBuilder进行初始化赋值
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;
}
XMLMapperEntityResolver
根据类中dtd文件对mybatis-config.xml中各种标签进行规范
public class XMLMapperEntityResolver implements EntityResolver {
//根据dtd文件校验mybatis-config.xml规范,看xml中标签是否在dtd中存在等
private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";
private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
//省略部分代码。。。
}
Configuration
//初始化Configuration,注册别名
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);
}
XPathParser
/**
* XPath解析器,用的都是JDK的类包,封装了一下,使得使用起来更方便
*/
public class XPathParser {
// 文档对象
private final Document document;
// 是否开启验证
private boolean validation;
// 用于加载本地DTD文件
private EntityResolver entityResolver;
// mybatis-config.xml中properties标签定义的键值对集合
private Properties variables;
// Xpath对象
private XPath xpath;
//省略部分代码···
}
创建SqlSessionFactory
前两步已经将mybatis-config.xml加载到了内存中,并且初始化了Configuration对象,接下来会执行parse方法,方法中会解析xml中的属性节点,为Configuration对象赋值。
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 前戏:准备工作:将配置文件加载到内存中并生成一个document对象 ,同时初始化Configuration对象
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.
}
}
}
// 解析配置
public Configuration parse() {
// 根据parsed变量的值判断是否已经完成了对mybatis-config.xml配置文件的解析
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 在mybatis-config.xml配置文件中查找<configuration>节点,并开始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
parseConfiguration
解析xml下各个节点。
private void parseConfiguration(XNode root) {
try {
//解析configuration下的properties节点
propertiesElement(root.evalNode("properties"));
//解析setting节点
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 设置vfsImpl字段
loadCustomVfs(settings);
loadCustomLogImpl(settings);
//解析类型别名,注册到typeAlias集合
typeAliasesElement(root.evalNode("typeAliases"));
//解析插件
pluginElement(root.evalNode("plugins"));
//对象工厂
objectFactoryElement(root.evalNode("objectFactory"));
//对象包装工厂
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//反射工厂
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//设置具体属性到configuration
settingsElement(settings);
//解析environments,
environmentsElement(root.evalNode("environments"));
// databaseIdProvider
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers节点
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
properties标签解析
根据properties中resource和url属性(resource=“db.properties”),进行资源加载,并放入hashTable中,并更新XPathParser和configuration中Variables对象
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//解析Properties子标签property中name和value属性,记录到Properties
Properties defaults = context.getChildrenAsProperties();
//获取resource属性
String resource = context.getStringAttribute("resource");
//获取url属性
String url = context.getStringAttribute("url");
//resource和url有一个
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = configuration.getVariables();
//与Configuration对象中Properties对象(继承了HashTable)合并
if (vars != null) {
defaults.putAll(vars);
}
//更新XPathParser和configuration中Variables对象
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
settings标签解析
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
// 解析settings子节点的name和value属性,并返回properties对象
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
// 创建configuration对应的MetaClass对象
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
// 检测Configuration中是否定义了key指定属性的setter方法
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
typeAliases别名标签解析
获取别名,并注册到typeAliases集合(Map<String, Class<?>>)中。
private void typeAliasesElement(XNode parent) {
if (parent != null) {
// 处理全部子节点
for (XNode child : parent.getChildren()) {
// 处理package节点
if ("package".equals(child.getName())) {
// 获取指定的包名
String typeAliasPackage = child.getStringAttribute("name");
// 通过TypeAliasRegistry扫描指定包中所有的类,并解析@Alias注解,完成别名注册
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
// 处理typeAlias节点
} else {
// 获取指定的别名
String alias = child.getStringAttribute("alias");
// 获取别名对应的类型
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
// 根据Class名字来注册类型别名
// 调用TypeAliasRegistry.registerAlias
if (alias == null) {
// 扫描@Alias注解,完成注册
typeAliasRegistry.registerAlias(clazz);
} else {
// 注册别名
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
registerAliases
//扫描并注册包下所有继承于superType的类型别名
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
// 查找指定包下的superType类型类
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
for (Class<?> type : typeSet) {
// 过滤掉内部类、接口以及抽象类
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
}
//注册类型别名
public void registerAlias(Class<?> type) {
// 类的简单名称(不包含包名)
String alias = type.getSimpleName();
// 读取@Alias注解
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
// 检测此别名不存在后,会将其记录到typeAlias集合中
registerAlias(alias, type);
}
//注册类型别名
public void registerAlias(String alias, Class<?> value) {
// 检测alias为null,则直接抛出异常
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// issue #748
// 将别名转换为小写
String key = alias.toLowerCase(Locale.ENGLISH);
// 检测别名是否已经存在
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
}
// 注册别名
typeAliases.put(key, value);
}
TypeAliasRegistry
/**
* 类型别名注册机
*/
public class TypeAliasRegistry {
private final Map<String, Class<?>> typeAliases = new HashMap<>();
public TypeAliasRegistry() {
//构造函数里注册系统内置的类型别名
registerAlias("string", String.class);
//基本包装类型
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
//基本数组包装类型
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class);
//加个下划线,就变成了基本类型
registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class);
//加个下划线,就变成了基本数组类型
registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class);
//日期数字型
registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class);
registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class);
//集合型
registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class);
//还有个ResultSet型
registerAlias("ResultSet", ResultSet.class);
}
//省略部分代码。。。
}
settingsElement
为configuration设置初始值。
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
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));
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));
configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
}
environmentsElement
为environment赋值,并且根据参数创建事务工厂和DB工厂。初始化Environment对象
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
//未指定XMLConfigBuilder.environment字段,则使用default
if (environment == null) {
environment = context.getStringAttribute("default");
}
//遍历子节点,
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
//与XmlConfigBuilder.environment字段匹配
if (isSpecifiedEnvironment(id)) {
//创建事务工厂
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//创建DB工厂
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//创建Environment对象
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
//设置configuration中environment变量
configuration.setEnvironment(environmentBuilder.build());
break;
}
}
}
}
mapperElement!!!
解析mapper标签,并添加到mapperRegistry中,
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
//获取到package子节点
if ("package".equals(child.getName())) {
//获取name属性值
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//获取mapper标签中resource、url、class属性,三个属性互斥
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
//如果使用resource
if (resource != null && url == null && mapperClass == null) {
// 创建XMLMapperBuilder对象,解析映射配置文件
ErrorContext.instance().resource(resource);
try(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);
try(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属性,则注册到mapperRegistry中。
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.");
}
}
}
}
}
addMappers
将接口添加到knownMappers(Map<Class<?>, MapperProxyFactory<?>>)中,并进行解析。
public <T> void addMapper(Class<T> type) {
//如果是接口类型
if (type.isInterface()) {
//判断是否在knowsMapper中
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//将mapper接口对应的Class对象和MapperProxyFactory添加到knownMappers中
knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
//如果过程中出现了异常,还需要从knownMappers中移除。
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
MapperAnnotationBuilder
将注解接口封装成一个对象,可通过注解进行识别。
public class MapperAnnotationBuilder {
private static final Set<Class<? extends Annotation>> statementAnnotationTypes = Stream
.of(Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class,
InsertProvider.class, DeleteProvider.class)
.collect(Collectors.toSet());
}
parse
解析mapper.xml
public void parse() {
String resource = type.toString();
//查看是否加载过该接口
if (!configuration.isResourceLoaded(resource)) {
//检查是否加载过对应的mapper.xml,如果没加载过,则创建XMLMapperBuilder对象解析对应的映射文件
loadXmlResource();
//放到loadedResources集合中
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
// 解析@CacheNamespace注解
parseCache();
// 解析@CacheNamespaceRef注解
parseCacheRef();
for (Method method : type.getMethods()) {
if (!canHaveStatement(method)) {
continue;
}
if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
&& method.getAnnotation(ResultMap.class) == null) {
parseResultMap(method);
}
try {
//解析@SelectKey,@ResultMap等注解,并创建MappedStatement对象
parseStatement(method);
} catch (IncompleteElementException e) {
//解析出错,放入incompleteMethods中保存
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
// 遍历incompleteMethods集合中记录的未解析的方法,并重新进行解析
parsePendingMethods();
}
loadXmlResource
加载mapper.xml
//获取到对应的xml文件并进行加载解析
private void loadXmlResource() {
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
//获取对应的mapper.xml
String xmlResource = type.getName().replace('.', '/') + ".xml";
// 将xml转换成stream流
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
// Search XML mapper that is not in the module but in the classpath.
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
// ignore, resource is not required
}
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
xmlParser.parse
解析mapper标签
public void parse() {
//判断是否加载过该文件
if (!configuration.isResourceLoaded(resource)) {
//解析mapper.xml文件中的mapper标签
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
configurationElement
private void configurationElement(XNode context) {
try {
//获取到namespace
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//设置当前命名空间
builderAssistant.setCurrentNamespace(namespace);
//解析cache-ref
cacheRefElement(context.evalNode("cache-ref"));
//解析cache
cacheElement(context.evalNode("cache"));
//解析parameterMap节点
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//返回resultMap节点
resultMapElements(context.evalNodes("/mapper/resultMap"));
//解析sql节点
sqlElement(context.evalNodes("/mapper/sql"));
//解析select、insert、update等标签属性节点
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);
}
}
buildStatementFromContext
解析SQL
private void buildStatementFromContext(List<XNode> list) {
//看DatabaseID是否为null,都是调用buildStatementFromContext方法
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
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 {
//核心,解析SQL语句
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
parseStatementNode
解析mapper.xml中各个标签、参数类型、返回类型等。并构建MappedStatement
public void parseStatementNode() {
//Id -》 方法名 以及databaseId属性。
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
//如果databaseId值不匹配,退出。
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
// 根据SQL节点的名称决定其SqlCommandType
String nodeName = context.getNode().getNodeName();
//SqlCommandType 枚举类,有SELECT、UPDATE、INSERT、DELETE和FLUSH等属性
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//看是否是select标签
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
// 在解析SQL语句之前,先处理其中的include节点
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
//参数类型
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
//脚本语言
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
//解析之前,先解析selectKey节点
processSelectKeyNodes(id, parameterTypeClass, langDriver);
KeyGenerator keyGenerator;
//获取selectKey节点对应的SelectKeyGenerator的id
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
//SQL节点下存在SelectKey节点
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
//解析sqlSource 一般是DynamicSqlSource
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
//获取语句类型 共STATEMENT, PREPARED(预处理,防止SQL注入), CALLABLE(存储过程)
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
//获取每次批量返回行数
Integer fetchSize = context.getIntAttribute("fetchSize");
//超时时间
Integer timeout = context.getIntAttribute("timeout");
//引入外部parameterMap
String parameterMap = context.getStringAttribute("parameterMap");
//结果类型
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
//返回类型
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
//结果集类型 FORWARD_ONLY、SCROLL_INSENSITIVE、SCROLL_SENSITIVE中的一种
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
//(仅对insert有用)标记一个属性,Mybatis会通过getGeneratedKeys或者通过insert语句的selectKey子元素设置它的值
String keyProperty = context.getStringAttribute("keyProperty");
//(仅对insert有用)标记一个属性,Mybatis会通过getGeneratedKeys或者通过insert语句的selectKey子元素设置它的值
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
//通过builderAssistant创建MappedStatement对象
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
MappedStatement
使用构造者模式创建MappedStatement,类中属性包含整个SQL名、接口Dao名,参数值,返回类型等。为后面动态代理创建Dao对象,执行对应方法做准备。
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
//id为对应的Dao加方法名
id = applyCurrentNamespace(id, false);
//是否是select类型
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
//构造者模式
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
//构造者模式创建MappedStatement
MappedStatement statement = statementBuilder.build();
//将statement放入mappedStatements( Map<String, MappedStatement>)中,key为Dao的全限定名+方法名组成
//因为Dao是接口,需要动态代理进行创建,具体要执行的Dao中方法是哪一个SQL语句,放入mappedStatements后,可在其中获取。
configuration.addMappedStatement(statement);
return statement;
}
流程图
加载mybatis-config.xml,并转化成Stream形式,通过SqlSessionFactoryBuilder解析Mybatis-config.xml,解析各个标签,并构建Configuration全局配置类,解析mapper标签时,会找到对应的mapper.xml和对应的Dao接口,今儿解析mapper.xml标签,并将Dao类型放入MapperRegister中,为动态代理做准备,并解析mapper.xml中标签,将属性通过构造者模式,创建MappedStatement类。