Mybatis的配置文件介绍
从前面的几篇文章,我们简单的介绍了mybatis的使用,以及如何用mybatis实现简单的增删改查。从这篇文章开始,我们将从其核心配置文件入手,对Mybatis支持的核心配置文件进行简单详细的描述。之前的文章中我们在代码中写了一个MybatisUtils类,我们具体看下其代码:
public class MybatisUtils {
private static SqlSessionFactory getSessionFactory() {
SqlSessionFactory sessionFactory = null;
String resource = "mybatis-config.xml";
try {
sessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader(resource));
} catch (IOException e) {
e.printStackTrace();
}
return sessionFactory;
}
/**
* 获取SqlSession
* @return SqlSession
*/
public static SqlSession getSqlSession() {
return getSessionFactory().openSession(true);
}
}
这段代码是我们在使用mybatis前的配置初始化过程,我们通过阅读其源码来逐步了解内部实现原理。这段代码首先是根据指定的配置文件的路径找到配置文件,读取配置文件的内容。接下来就是SqlSessionFactoryBuilder.build方法进行配置文件的处理,我们详细看下此类的代码:
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, 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.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
通过此类的源码我们看到在这个类中,支持多种构造SqlSessionFactory的方法。可以只传入mybatis配置文件,也可以同时传入properties配置文件替代mybatis配置文件中的元素标签,另外也支持传入环境参数envirmont参数。
我们跟随着源码继续往下看,在多个build方法最终都是用到了下面的方法:
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
我们可以看到这里创建了一个XMLConfigBuilder类实例,通过他来对mybatis配置文件进行解析。我们看下此类的构造函数:
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
我们看到内部用了XPathParser,继续跟踪代码:
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
分析代码可以了解到,此方法主要就是读取配置文件到Document中。接下来我们回到SqlSessionFactoryBuilder的build方法中,build(parser.parse())这段代码是具体解析配置文件的入口,我们进入到parser.parse()方法内部:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(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);
}
}
从这里看出,配置文件是以configuration为根节点,在根节点之下有多个子节点,它们分别为:properties、settings、typeAliases、plugins、objectFactory、objectWrapperFactory、environments、databaseIdProvider、typeHandlers、mappers。
这里需要注意mybatis在进行配置文件解析时,对于配置文件中的各个属性的先后顺序是有要求的,不能颠倒。