Mybatis源码分析
一,mybatis执行代码
//读取配置文件
InputStream is = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactoryBuilder builder=new SqlSessionFactoryBuilder();
//获取SqlSessionFactory
SqlSessionFactory sessionFactory = builder.build(is);
//获取SqlSession
SqlSession openSession = sessionFactory.openSession();
//获取代理对象
UserDao userDao = openSession.getMapper(UserDao.class);
User user=userDao.findById(1);
//资源关闭
openSession.close();
is.close();
二,源码分析
1.SqlSessionFactory sessionFactory = builder.build(is)方法执行流程,这个方法做了些什么操作?
源码:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//使用XMLConfigBuilder来解析mybatis全局的配置文件
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//解析全局配置文件根据Configuration对象返回了一个默认的DefaultSqlSessionFactory
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.
}
}
}
1.1.先看看这个类的创建XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
源码
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//调用父类构造器初始化一个Configuration
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
//parsed默认为为fasle
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
1.2.parser.parse()
源码
public Configuration parse() {
//parsed默认为false
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
//下面将parsed设为true,表示XMLConfigBuilder parseConfiguration()方法只能执行一次,真正解析操作解析一次
parsed = true;
//解析配置文件
parseConfiguration(parser.evalNode("/configuration"));
//返回一个Configuration类的对象,里面 分装好了mybatis的配置文件信息
return configuration;
}
1.2.1parseConfiguration(parser.evalNode("/configuration"));
源码
//对mybatis配置文件中的各个标签进行解析,将xml转换成javaBean的过程,并存储在全局的容器 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"));
//解析mappers标签 会将Mapper.xml 也解析完加入到 Configuration中
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
补充:root.evalNode("/configuration"),这个方法是找出configuration标签里所有的一级子标签
XNode类:
public List<XNode> evalNodes(String expression) {
return xpathParser.evalNodes(node, expression);
}
XPathParser类:
public List<XNode> evalNodes(String expression) {
return evalNodes(document, expression);
}
public List<XNode> evalNodes(Object root, String expression) {
List<XNode> xnodes = new ArrayList<XNode>();
NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET);
for (int i = 0; i < nodes.getLength(); i++) {
xnodes.add(new XNode(this, nodes.item(i), variables));
}
return xnodes;
}
1.2.1.1标签解析environmentsElement(root.evalNode(“environments”)); 其标签可以自己去看看
这里 就 不一一列出了
源码
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
//遍历子标签去解析
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
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);
//会将解析完标签后的数据存入Configuration 的对象中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
上面执行完了build(parser.parse()); 里的parser.parse() 方法,解析了配置文件,及*Mapper.xml文件
下面会具体对解析*Mapper.xml文件进行源码解读,解析的内容都存储在全局容器Configuration中并返回
1.3:buile() 创建SqlSessionFactory
源码
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(Configuration config) {
//根据Configuration返回一个默认的 DefaultSqlSessionFactory
return new DefaultSqlSessionFactory(config);
}
上述步骤SqlSessionFactory 已经创建好了mybatis配置 文件解析完存储在Configuration中了,把xml转成javaBean对象
2.SqlSession openSession = sessionFactory.openSession();
源码
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
//获取Configuration中的Environment,这些Environment(环境信息)都是包括使用哪种数据库,连接数据库的信息,事务
final Environment environment = configuration.getEnvironment();
//根据环境信息关于事务的配置获取事务工厂
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//environment.getDataSource() 根据环境信息关于数据库的配置获取数据源,添加到事务中
//level 事务隔离级别;autoCommit 是否自动提交 在openSession()中可以传入这个参数值
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//从configuration中获取一个执行器实例
final Executor executor = configuration.newExecutor(tx, execType);
//根据configuration,executor,autoCommit创建一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
//DefaultSqlSession构造器方法 dirty默认设为false
public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
this.configuration = configuration;
this.executor = executor;
this.dirty = false;
this.autoCommit = autoCommit;
}
sessionFactory.openSession()创建了事务管理工厂,创建了Executor
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
三种执行器: SIMPLE
BATCH
REUSE
上述俩个方法的操作
步骤一:从主配置文件流中读取文件的各个节点信息并存放到Configuration对象中。读取mappers节点的引用文件,并将这些文件的各个节点信息存放到Configuration对象
步骤二:根据Configuration对象的信息获取数据库连接,并设置连接的事务隔离级别等信息,将经过包装数据库连接对象SqlSession接口返回,DefaultSqlSession是SqlSession的实现类,所以这里返回的是DefaultSqlSession,SqlSession接口里面就是对外提供的各种数据库操作。
3.获取代理对象 UserDao userDao = openSession.getMapper(UserDao.class);
源码
public <T> T getMapper(Class<T> type) {
return configuration.<T>getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//new 一个代理对象创建工厂
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//创建代理对象返回
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
3.1mapperProxyFactory.newInstance(sqlSession);
源码
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
//创建好了代理对象返回 根据sqlSession:DefaultSqlSession mapperInterface:UserDao methodCache:方法缓存
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
4.User user=userDao.findById(1); 方法执行流程
源码 :代理对象invoke 方法的执行
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//判断是不是Object的方法
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
//将方法 缓存起来
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
4.1.mapperMethod.execute(sqlSession, args);
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
//参数转换与SQL进行绑定,执行SQL
switch (command.getType()) {
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
//SQL执行
result = sqlSession.selectOne(command.getName(), param);
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
//解析转换获取参数
public Object convertArgsToSqlCommandParam(Object[] args) {
return paramNameResolver.getNamedParams(args);
}
4.2sqlSession.selectOne(command.getName(), param);
源码
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
//方法执行
List<T> list = this.<T>selectList(statement, parameter);
if (list.size() == 1) {
//结果返回
return list.get(0);
} else if (list.size() > 1) {
throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
} else {
return null;
}
}
//方法执行跳转
public <E> List<E> selectList(String statement, Object parameter) {
return this.selectList(statement, parameter, RowBounds.DEFAULT);
}
//方法执行
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
//MappedStatement 它存储了一个 sql 对应的所有信息
MappedStatement ms = configuration.getMappedStatement(statement);
//用方法执行器来执行
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
至此方法执行发,mybatis运行源码流程
5.仔细分析一下**Mapper.xml文件 的解析过程mapperElement(root.evalNode(“mappers”));
源码
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 开始遍历每1个节点
for (XNode child : parent.getChildren()) {
/*
<mappers>
<mapper resource=""></mapper>
<mapper url=""></mapper>
<package name="com.yj.dao" ></package>
</mappers>
对这三个标签的解析获取
*/
// 如果是package命名
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
// 一般从这里开始执行
// 获取resource的属性值 全局配置文件中 mapper标签,属性resource的值
String resource = child.getStringAttribute("resource");
// 获取url的属性值
String url = child.getStringAttribute("url");
// 获取class的属性值
String mapperClass = child.getStringAttribute("class");
// 如果是指定了resource,其它都没有指定,就走这个分支
if (resource != null && url == null && mapperClass == null) {
// 将文件路径保存到上下文中,ThreadLocal型
ErrorContext.instance().resource(resource);
// 读取文件流
InputStream inputStream = Resources.getResourceAsStream(resource);
//
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
configuration.getSqlFragments());
//解析 *Mapper.xml
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.");
}
}
}
}
}
5.1mapperParser.parse();
源码
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
configurationElement(parser.evalNode("/mapper"));
源码
private void configurationElement(XNode context) {
try {
//获取namespace 判断是否为null 为null抛出异常
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//对非增删改查标签的解析
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
//对增删改查标签的解析
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
buildStatementFromContext(context.evalNodes(“select|insert|update|delete”));
源码
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
buildStatementFromContext(list, null);
}
//跳转
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
//遍历拿到的所有select|insert|update|delete节点,然后调用statementParser.parseStatementNode()进行解析:
for (XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
statementParser.parseStatementNode();
源码
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
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);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
//这里面就是对整个mapper文件进行了解析操作,包括拿到id;sql语句呀等等,然后将其组装成MappedStatement:
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
builderAssistant.addMappedStatement方法下面部分代码:
id = applyCurrentNamespace(id, false);
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 statement = statementBuilder.build();
//将MappedStatement 添加到configuration中 一namespace 为key MappedStatement 为value
configuration.addMappedStatement(statement);
return statement;
bindMapperForNamespace() :
源码
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}