通过MyBatis的简单案例来看阅读源码
// 这里主要是MyBatis的测试类,是源码阅读的入口:
@Test
public void test01() throws IOException {
// 1、获取sqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
UserMapper mapper = openSession.getMapper(UserMapper.class);
// 4执行增删改查的方案
User User = mapper.getUserById(1);
System.out.println(mapper);
System.out.println(User);
} finally {
openSession.close();
}
}
一、获取SqlSessionFactory对象的过程
测试中的主要代码是:
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);
流程图:
1.这里先是调用了SqlSessionFactoryBuilder的build()方法:
进入SqlSessionFactoryBuilder类的build
/*
build是一个重载的方法,实际都是都调用build(InputStream inputStream, String environment, Properties properties) 这个方法
*/
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
/*
实际调用的build(InputStream inputStream, String environment, Properties properties)
*/
public SqlSessionFactory build(InputStream inputStream,
String environment, Properties properties) {
try {
// 这里创建了XMLConfigBuilder对象,用于解析xml文件的
XMLConfigBuilder parser =
new XMLConfigBuilder(inputStream, environment, properties);
// 这里调用的XMLConfigBuilder的实例对象parser 的parse()方法
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类的 parse() 方法:
// 这是XMLConfigBuilder的parse方法,返回的Configuration对象
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
/*
调用同一个类中的parseConfiguration()方法中,调用解析器的evalNode()方法,解析MyBatis配置文件中的configuration
*/
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
在上面的 parse() 调用了同类的 parseConfiguration() 方法
// 这是XMLConfigBuilder的 parseConfiguration 方法
private void parseConfiguration(XNode root) {
try {
/*
这里先找到根节点的每一子节点,像properties、typeAliases、plugins等依次进行解析并设置
*/
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
typeAliasesElement(root.evalNode("typeAliases"));
/*
这里是对全局配文件中的plugins进行解析并设置,
进入pluginElement()看一下
*/
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
/*
这里就是把配置文件中的setting进行解析并设置
*/
settingsElement(root.evalNode("settings"));
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
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);
}
}
上面的parseConfiguration() 有对MyBatis配置文件的解析和设置,我们这里重点看一下三个
- plugins
- settings
- mappers
1、先是plugins标签属性的解析和设置:进入XMLConfigBuilder的**pluginElement()**方法
/*
接下来是进入MLConfigBuilder 中的pluginElement()方法
*/
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
//如果配置了<plugins/>节点
for (XNode child : parent.getChildren()) {
// 解析plugins节点中的interceptor
String interceptor = child.getStringAttribute("interceptor");
//解析interceptor标签中的属性
Properties properties = child.getChildrenAsProperties();
//解析interceptor并创建相应的interceptor实现类的对象
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//解析相应的interceptor配置类的相应属性
interceptorInstance.setProperties(properties);
configuration.addInterceptor(interceptorInstance);
}
}
}
2、接着是settings标签解析和设置:进入XMLConfigBuilder的**settingsElement()**方法
/*----------------------------重点分割线:start---------------------------------------*/
/*
这里进入setingsElemnt()进行看一下,把在全局配置文件中可配置属性进行解析并设置,如果没有设置的属性,MyBatis会设置一个默认值。
*/
private void settingsElement(XNode context) throws Exception {
if (context != null) {
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
MetaClass metaConfig = MetaClass.forClass(Configuration.class);
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).");
}
}
/*----------------------------重点分割线:end---------------------------------------*/
/*
下面对在全局配置文件中可配置属性进行解析并设置,如果没有设置的属性,MyBatis会设置一个默认值
*/
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
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"), true));
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.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.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
}
3、进入 mappers标签的解析和设置:进入XMLConfigBuilder的mapperElement() 方法
/*
这里进入mapperElement()进行看一下,把在全局配置文件中我们配置的Mapper相关的信息进行解析*/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
/*----------------------------重点分割线:start---------------------------------------*/
for (XNode child : parent.getChildren()) {
// 判断用了包扫描的方式
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
// 本测试中使用的是resource标签进行设置的
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 的解析器,是对XxxMapper.xml文件进行解析*/
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 调用了XMLMapperBuilder类中的方法,下面进入parse()
mapperParser.parse();
/*----------------------------重点分割线:end---------------------------------------*/
} 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.");
}
}
}
}
}
上面的解析mappers标签中调用了XMLMapperBuilder类中的 parse() 方法,下面进入parse() :
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//这里调用同类中configurationElement()方法,接下来进入configurationElement()方法
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
}
调用了XMLMapperBuilder类中的 configurationElement() 方法:
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (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"));
/* 这里返回了Mapper中select|insert|update|delete的List<XNode> list集合,
下面进入buildStatementFromContext方法*/
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
上面调用了XMLMapperBuilder的buildStatementFromContext() 方法:
private void buildStatementFromContext(List<XNode> list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
// 这里调用了 buildStatementFromContext的重载方法
buildStatementFromContext(list, null);
}
// 重置的方法
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
for (XNode context : list) {
/*
创建XMLStatementBuilder 对象,用于解析Mapper.xml中的增删改查的SQL语句
*/
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 这里调用了SQL节点标签解析的方法:parseStatementNode()
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
上面调用了XMLStatementBuilder 类中SQL节点标签解析的方法:parseStatementNode()
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) return;
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
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());
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
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))
? new Jdbc3KeyGenerator() : new NoKeyGenerator();
}
/*----------------------------重点分割线:start---------------------------------------*/
/*
通过解析得到Mapper中statement语句的所有信息,
调用addMappedStatement()添加一个MappedStatement,
一个MappedStatement就代表一个增删改查方法相对应的标签的详细信息,
这些最终都保存在configuation对象中。
*/
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
/*----------------------------重点分割线:end---------------------------------------*/
上面使用到MapperBuilderAssistant 类的addMappedStatement()方法,该类继承BaseBuilder类
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 = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
statementBuilder.resource(resource);
statementBuilder.fetchSize(fetchSize);
statementBuilder.statementType(statementType);
statementBuilder.keyGenerator(keyGenerator);
statementBuilder.keyProperty(keyProperty);
statementBuilder.keyColumn(keyColumn);
statementBuilder.databaseId(databaseId);
statementBuilder.lang(lang);
statementBuilder.resultOrdered(resultOrdered);
statementBuilder.resulSets(resultSets);
setStatementTimeout(timeout, statementBuilder);
setStatementParameterMap(parameterMap, parameterType, statementBuilder);
setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);
MappedStatement statement = statementBuilder.build();
/*----------------------------重点分割线:start---------------------------------------*/
// 可以看到最终MappedStatement的实例还是保存在了configuration对象中
configuration.addMappedStatement(statement);
return statement;
}
/*----------------------------重点分割线:end---------------------------------------*/
二、获取sqlSession对象的过程
测试中的代码:
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
UserMapper mapper = openSession.getMapper(UserMapper.class);
整个流程的解析(流程图):
现在进入源码部分:
进入了DefaultSqlSessionFactory类,该类实现了SqlSessionFactory,进入:openSession() 方法
public SqlSession openSession() {
/* 调用openSessionFromDataSource()*/
return openSessionFromDataSource(
configuration.getDefaultExecutorType(), null, false);
}
实际调用的DefaultSqlSessionFactory类下面的重载的openSession() 方法
/ 在该类中Mapper进行了重载,最终实际调用的是下面的方法
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
/*从configuration中获取当前的环境*/
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
/*----------------------------重点分割线:start---------------------------------------*/
/*
创建一个Executor实现类的对象 ,这个对象非常重要。
Executor是一个接口
有SimpleExecutor、BatchExecutor和ReuseExecutor等实现类
是MyBatis中四个Executor、StatementHandler、ParamHandler、ResultSetHandler重要的对象之一.
*/
final Executor executor = configuration.newExecutor(tx, execType);
// 创建并返回了一个包含configuration、executor和autoCommit相关信息的DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
/*----------------------------重点分割线:end---------------------------------------*/
} 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();
}
}
上面调用了Configuration中的newExecutor() 方法:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
// ExecutorType有三种类型的分写SIMPLE、BATCH 、REUSE ,可以通过配置文件进行配置
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
/*----------------------------重点分割线:start---------------------------------------*/
/*
如果全局配置中配置了缓存,使用CachingExecutor对之前的对象进行包装
CachingExecutor在遇到查询的方法前,会现在本地缓存查询一下是否有数据,如果返回缓存中的数据,
如果没有,所有底层还是调用SimpleExecutor的对应方法。
*/
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
/* 这里调用了interceptorChain的pluginAll() */
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
/*----------------------------重点分割线:end---------------------------------------*/
上面创建完executor后还调用了interceptorChain.pluginAll(executor),对它进行包装,现在进入
InterceptorChain 查看:
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
// 会用Exector进行包装进入配置的每个interceptor 实现类中
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
三、获取接口的实现类对象
测试中的关键代码:
UserMapper mapper = openSession.getMapper(UserMapper.class);
流程图:
进入SqlSessionFactory类的getMapper() 方法:
public <T> T getMapper(Class<T> type) {
// 实际调用了Configuration的getMapper()方法
return configuration.<T>getMapper(type, this);
}
上面实际调用了Configuration的getMapper() 方法:
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 实际调用的MapperRegistry对象的getMapper方法
return mapperRegistry.getMapper(type, sqlSession);
}
上面实际调用的MapperRegistry对象的getMapper() 方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
/*Configuation中的MapperRegistry中的knownMappers获取到我们需要的
Mapper 类型的mapperProxyFactory
*/
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);
}
}
进入MapperProxyFactory的newInstance() 方法:
public class MapperProxyFactory<T> {
// 这个是上面调用的newInstance()方法
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
// 最终实际调用的是这个重载的方法
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
}
查看一下MapperProxy类,发现它实现了InvocationHandler接口,说明这个类是用于产生动态代理对象的类:
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperMethod> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
}
最后返回了MapperProxy的对象(即接口的代理对象)
四、使用Mapper接口的代理对象进行增删改查的流程
测试中的代码:
// 4执行增删改查的方案
User User = mapper.getUserById(1);
流程图:
进入源码部分:
进入MapperProxy类,执行invoke() 方法:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/*
如果Object类的方法,就直接放行,
*/
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
/*method包装成一个MapperMethod对象*/
final MapperMethod mapperMethod = cachedMapperMethod(method);
// 这里调用MapperMethod的execute()方法
return mapperMethod.execute(sqlSession, args);
}
上面调用了MapperMethod类,调用execute() 方法:
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
/*这是SQL语句类型进行判断INSERT 、UPDATE、DELETE、SELECT */
if (SqlCommandType.INSERT == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
} else if (SqlCommandType.UPDATE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
} else if (SqlCommandType.DELETE == command.getType()) {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
} else if (SqlCommandType.SELECT == command.getType()) {
/*在查询(SELECT)类型中会对返回类型进行检查*/
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
/*这是返回多个的结果*/
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
/*这是返回Map类型的*/
result = executeForMap(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
/*这是查询单个的,返回单个实体类对象的,调用的DefaultSqlSession
的selectOne()方法*/
result = sqlSession.selectOne(command.getName(), param);
}
} else {
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;
}
上面调用了DefaultSqlSession 类的selectOne() 方法:
public <T> T selectOne(String statement, Object parameter) {
// Popular vote was to return null on 0 results and throw exception on too many.
/*在这里看到实际是调用自己的selectList()方法*/
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;
}
}
进入该类的selectList()方法,该方法有很多的重载方法,最后调用的是下面的
selectList(String statement, Object parameter, RowBounds rowBounds) 方法:
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
/*
先从configuation中获取对应的MappedStatement,
然后传给executor对象,调用query进行查询
wrapCollection(parameter):这里对我们传入的参数进行包装
*/
MappedStatement ms = configuration.getMappedStatement(statement);
List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
return result;
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
看一下是如何包装我们传入的对象的:
private Object wrapCollection(final Object object) {
if (object instanceof List) {
StrictMap<Object> map = new StrictMap<Object>();
// 如果传入的集合对象是List集合,map中的key使用的是"list"
map.put("list", object);
return map;
} else if (object != null && object.getClass().isArray()) {
StrictMap<Object> map = new StrictMap<Object>();
// 如果传入的集合对象是数组,map中的key使用的是"array"
map.put("array", object);
return map;
}
return object;
}
上面对传入的参数包装后使用的Executor的query()的查询方法
Executor的实现类有BaseExecutor、SimpleExecutor、CachExecutor等,而调用query()实际是调用的是BaseExecutor的:
// BaseExecutor类中query()方案
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
/*
这里获取到了BoundSql 对象,
BoundSql :包含了SQL语句,参数信息、元数据参数等
*/
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
如果MyBatis中使用了缓存,会将SimpleExecutor包装成了CacheExecutor:进入CacheExecutor的Query方法:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
}
return list;
}
}
// 底层还是调用SimpleExecutor的query方法
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
最终还是调用了BaseExecutor类中重载的Query方法:
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) throw new ExecutorException("Executor was closed.");
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
/*
先从本地缓存中拿数据,*/
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
/*本地缓存中没有数据,调用queryFromDatabase方法*/
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
deferredLoads.clear(); // issue #601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue #482
}
}
return list;
}
调用同一个类中的queryFromDatabase() 方法:
/*
进入queryFromDatabase方法
*/
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
/*调用doQuery进行查一下*/
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
// 将查出的数据存入本地缓存中
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(key, parameter);
}
//返回查询的数据
return list;
}
在BaseExecutor中
//进入doQuery方法,该方法是抽象方法,在SimpleExecutor中进行了实现
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
因为SimpleExecutor类的实现doQuery方法:
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
//从这里可以看出MyBatis的底层还是使用的原生的JDBC
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
/*
创建一个StatementHandler对象,
是mybatis中四个重要对象之一,用于创建Statement对象
在创建的StatementHandler的时候,底层也同时创建创建的ParamHandler和ResultSetHandler这两个对象
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
/**/
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
StatementHandler对象是在Configuation对象中创建的,进入Configuation的newStatementHandler方法:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
/*这里创建了一个RoutingStatementHandler对象*/
StatementHandler statementHandler = new RoutingStatementHandler(executor,
mappedStatement,
parameterObject,
rowBounds,
resultHandler,
boundSql);
statementHandler = (StatementHandler)
/*
对象创建完了之后,还是会使用拦截器来包装StatementHandler
*/
interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
进入RoutingStatementHandler,查看是如创建的
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
// 这是statement,没有进行预编译的,不能防止SQL注入攻击
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
// 这是预编译的PrepareStatement,默认的是PREPARED
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
// 这是存储过程
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
创建PrepaStatementHandler后,使用它创建出Statement,调用SimpleExecutor的prepareStatement方法,进行预编译:
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection);
handler.parameterize(stmt);
return stmt;
}
上面调用了PrepareStatementHandler类中parameterize方法:
public void parameterize(Statement statement) throws SQLException {
/*
调用parameterHandler的setParameters,进行预编译参数设置
(parameterHandler是之前创建StatementHandler对象的时候就创建好了的)
parameterHandler是接口,实际是DefaultParameterHandler类的对象
*/
parameterHandler.setParameters((PreparedStatement) statement);
}
调用了DefaultParameterHandler类的setParameters:
public void setParameters(PreparedStatement ps) throws SQLException {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
/*最重要的是这里:使用TypeHandler 对象的setParameter方法进行设置预编译参数。*/
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}
调用DefaultResultSetHandler类的handleResultSets()方法:
public List<Object> handleResultSets(Statement stmt) throws SQLException {
ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
final List<Object> multipleResults = new ArrayList<Object>();
int resultSetCount = 0;
ResultSetWrapper rsw = getFirstResultSet(stmt);
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
validateResultMapsCount(rsw, resultMapCount);
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
String[] resultSets = mappedStatement.getResulSets();
if (resultSets != null) {
while (rsw != null && resultSetCount < resultSets.length) {
ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
if (parentMapping != null) {
String nestedResultMapId = parentMapping.getNestedResultMapId();
ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
handleResultSet(rsw, resultMap, null, parentMapping);
}
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
}
handleResultSets方法最终有间接调用到getPropertyMappingValue,代码中可以说明在处理结果映射的时候还是用的TypeHandler对象来处理返回结果的:
private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
throws SQLException {
if (propertyMapping.getNestedQueryId() != null) {
return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
} else if (propertyMapping.getResultSet() != null) {
addPendingChildRelation(rs, metaResultObject, propertyMapping);
return NO_VALUE;
} else if (propertyMapping.getNestedResultMapId() != null) {
// the user added a column attribute to a nested result map, ignore it
return NO_VALUE;
} else {
// 这里使用了TypeHandler类来处理结果集的映射
final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
return typeHandler.getResult(rs, column);
}
}