我们平常使用mybatis如下,主要分为四步:
<configuration>
<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>
<mappers>
<mapper resource="mapper.xml"/>
mappers>
configuration>
以及若干mapper.xml和接口
public static void main(String[] args) {
InputStream is = Test.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
//1、得到SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
//2、得到SqlSession
SqlSession session = factory.openSession();
//3、得到mapper
StuInfoMapper mapper = session.getMapper(StuInfoMapper.class);
//4、执行得到结果
StuInfo stu = mapper.getStuById("12345678");
System.out.println(stu);
session.close();
}
对于mybatis不太熟悉的可以和mybatis官方文档一起看看
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
new一个SqlSessionFactoryBuilder,调用build方法,实际是做了这些操作:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
//定义一个XMLConfigBuilder(基于XPathParser)
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//使用parse解析,将解析结果给了build,再次调用build,生成SqlSessionFactory并返回
var5 = this.build(parser.parse());
...//此处省略若干
return var5;
}
public SqlSessionFactory build(Configuration config) {
//生成一个DefaultSqlSessionFactory返回
return new DefaultSqlSessionFactory(config);
}
总结一下这个过程:
1、 定义一个XMLConfigBuilder解析配置文件(mybatis-config.xml),解析之后生成一个Configuration ,存放配置文件中的全部信息。
2、把配置文件给默认的SqlSessionFactory,返回生成的DefaultSqlSessionFactory
核心是解析配置文件
public Configuration parse() {
//省略若干代码
//将文件中的configuration标签的内容拿出来作为node
//调用parseConfiguration解析这个node的内容
this.parseConfiguration(this.parser.evalNode("/configuration"));
}
对configuration标签记不清楚的可以回到最开始看一下配置文件,这个标签包含了配置文件全部的内容。
configuration标签下可以配置properties,settings等标签(必须按顺序配置)。
按顺序将每一个标签取出作为node,存入不同的Element进行处理
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
//加载vfs
this.loadCustomVfs(settings);
//加载日志
this.loadCustomLogImpl(settings);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
//设置set标签的各个属性的值(默认值|用户定义的值)
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
//我们常用的mapper标签
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
常用的就是mapppers标签,所以专门说一下。
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
//遍历下面的每一个mapper标签
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
//如果是package属性,则configuration.addMappers
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
//如果是resource或者url 属性,则用XMLMapperBuilder(基于XPathParser)对对应的mapper.xml文件进行解析
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream inputStream;
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
inputStream = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
inputStream = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if (resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
//如果是mapperClass(接口),则也直接添加configuration.addMappers
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
1、package属性或者mapperClass(接口),则添加->configuration.addMappers
2、resource或者url 属性,则用XMLMapperBuilder进行解析
public void addMappers(String packageName) {
this.mapperRegistry.addMappers(packageName);
}
//经过一系列的操作,将string的包名,变为Class type
public <T> void addMapper(Class<T> type) {
this.mapperRegistry.addMapper(type);
}
//然后添加
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
//省略若干代码
//给configuration的mapperRegistry的knownMappers存入{接口,MapperProxyFactory}键值对
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
}
}
public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
//根据全部标签,设置不同的Element,与上面解析配置文件相同
this.configurationElement(this.parser.evalNode("/mapper"));
//给configuration中添加
this.configuration.addLoadedResource(this.resource);
//将接口与xml文件绑定,并添加进configuration
this.bindMapperForNamespace();
}
//解析ResultMap
this.parsePendingResultMaps();
//解析缓存引用
this.parsePendingCacheRefs();
//解析sql语句
this.parsePendingStatements();
}
//将接口与xml文件绑定,并添加进configuration
private void bindMapperForNamespace() {
String namespace = this.builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException var4) {
}
if (boundType != null && !this.configuration.hasMapper(boundType)) {
this.configuration.addLoadedResource("namespace:" + namespace);
//最后还是进行了configuration的添加
this.configuration.addMapper(boundType);
}
}
}
//解析mapper.xml的全部标签
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace != null && !namespace.equals("")) {
this.builderAssistant.setCurrentNamespace(namespace);
this.cacheRefElement(context.evalNode("cache-ref"));
this.cacheElement(context.evalNode("cache"));
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
this.sqlElement(context.evalNodes("/mapper/sql"));
//增删改查标签
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} else {
throw new BuilderException("Mapper's namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
}
}
//list全部的增删改查标签
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
Iterator var3 = list.iterator();
while(var3.hasNext()) {
XNode context = (XNode)var3.next();
//用XMLStatementBuilder 解析每一个标签
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser);
}
}
}
public void parseStatementNode() {
String id = this.context.getStringAttribute("id");
String databaseId = this.context.getStringAttribute("databaseId");
if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
Integer fetchSize = this.context.getIntAttribute("fetchSize");
String parameterMap = this.context.getStringAttribute("parameterMap");
//获取全部属性,timeout,parameterType,resultMap,lang等
//根据获得的属性值,对一些内容进行相应设置
//此处已省略
//将获得的全部属性值添加
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
//addMappedStatement方法
/*
最终会生成MappedStatement ,并将其存入configuration
MappedStatement statement = statementBuilder.build();
this.configuration.addMappedStatement(statement);
*/
当处理完配置文件的全部标签、属性,以及定义的mapper.xml的全部内容后,就结束了,这个时候我们看一下configuration
首先它存放了非常多的信息
其中有一个这个,这是我们之前存进去的接口,以后会用到很重要
开始第二步,得到sqlsession