1、MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
2、MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
3、MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
https://mybatis.org/mybatis-3/z h/index.html
上面链接是官网链接,很多配置使用信息可以从那里获取 。
Mybatis的定义也写得很清楚它的具体功能即实现的目的,我们看源码也是围绕着这几点来看。我个人认为看源码要从宏观大的结构,再到具体的微观层面,看完之后再总结,在回归头来微观细节方面是如何实现宏观框架的。
我认为Mybatis可以从以下三个方面去看:
Mybatis宏观模块:
1、Mybatis环境配置信息模块对应于(mybatis-config.xml)中相应的配置信息
2、Mapper接口信息和mapper.xml文件(这里也可以是注解)方法与sql绑定问题
3、最后是sql的执行,我么调用相应的接口方法如何被执行
在我看来如果你能搞明白这三个点,你对mybatis就有一个比较深的认识。接下来我也是从这三个点去阅读源码
**SqlSessionFactory:**每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或通过Java的方式构建出 SqlSessionFactory 的实例。SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,建议使用单例模式或者静态单例模式。一个SqlSessionFactory对应配置文件中的一个环境(environment),如果你要使用多个数据库就配置多个环境分别对应一个SqlSessionFactory。
**SqlSession:**SqlSession是一个接口,它有2个实现类,分别是DefaultSqlSession(默认使用)以及SqlSessionManager。SqlSession通过内部存放的执行器(Executor)来对数据进行CRUD。此外SqlSession不是线程安全的,因为每一次操作完数据库后都要调用close对其进行关闭,官方建议通过try-finally来保证总是关闭SqlSession。
**Executor:**Executor(执行器)接口有两个实现类,其中BaseExecutor有三个继承类分别是BatchExecutor(重用语句并执行批量更新),ReuseExecutor(重用预处理语句prepared statement,跟Simple的唯一区别就是内部缓存statement),SimpleExecutor(默认,每次都会创建新的statement)。以上三个就是主要的Executor。通过下图可以看到Mybatis在Executor的设计上面使用了装饰器模式,我们可以用CachingExecutor来装饰前面的三个执行器目的就是用来实现缓存。
**MappedStatement:**MappedStatement就是用来存放我们SQL映射文件中的信息包括sql语句,输入参数,输出参数等等。一个SQL节点对应一个MappedStatement对象。
执行过程
https://blog.csdn.net/u010890358/article/details/80665753
通过第一节,我们将debug源码的环境搭建好,开始debug查看源码。看源码的过程中,对于上层的用F7进入每个方法查看调用,对于rt.jar底层方法的调用直接使用F8,只要知道调用方法返回的结果即可。
断点查看Mybatis如何获取配置文件
//2、初始化mybatis,创建SqlSessionFactory类实例
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties) 调用
org.apache.ibatis.parsing.XPathParser#createDocument()
org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
利用XMLConfigBuilder方法中的parseConfiguration方法解析相关的 ,inputStream流是mybatis-config.xml字节流,XMLConfigBuilder的parse()方法解析mybatis-config.xml节点得到Configuration配置类。下面有详细的解析过程。
this.environmentsElement(root.evalNode("environments"));
这个方法得到环境配置文件
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
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"));
this.settingsElement(settings);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
我们看下他是如何解析environments,F7进入后我们发现
org.apache.ibatis.builder.xml.XMLConfigBuilder#environmentsElement
方法environmentsElement 代码如下:
//解析environments
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (this.environment == null) {
this.environment = context.getStringAttribute("default");
}
Iterator var2 = context.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String id = child.getStringAttribute("id");
if (this.isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
this.configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
//解析dataSource
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory)this.resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
} else {
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
}
DataSource的获取,首先通过XMLConfigBuilder#environmentsElement 方法得到配置文件(mybatis-config.xml)中的environments节点在获取enviroment节点,再通过XMLConfigBuilder#dataSourceElement解析dataSource中的内容,必将dataSource中的内容存在在Enviroment的DataSource变量中。
环境配置信息读取后存放在Confiuartion变量中
this.configuration.setEnvironment(environmentBuilder.build());
第一节中parseConfiguration方法解析mybatis文件有解析mappers节点的方法,进入该方法,代码如下:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
if ("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
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.");
}
Class<?> mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
看到这个了没
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
配置文件中的mapper resource ,resource还有自动扫包package其实可以有四种写法,但是解析方式各有写差异,我们默认用resource,有时间自己可以研究其他两种
<mappers>
<mapper resource="mapper/PaymentMapper.xml"/>
</mappers>
mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments()); 获取到mapper.xml资源文件,在利用
mapperParser.parse()解析xml文件;进入这个方法,解析mapper文件,进入 this.configurationElement(this.parser.evalNode("/mapper"));方法。在该方法中有解析mapper.xml相关属性节点的方法。(XXXXmapper.xml文件不熟悉的同学多看下标准xml)
public void parse() {
if (!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper"));
this.configuration.addLoadedResource(this.resource);
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}
this.configurationElement(this.parser.evalNode("/mapper"));方法详情
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);
}
}
注意最后的org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List
buildStatementFromContext(List list, String requiredDatabaseId);方法:
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
Iterator var3 = list.iterator();
while(var3.hasNext()) {
XNode context = (XNode)var3.next();
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
//解析sql语句,进入该方法
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser);
}
}
org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode
parseStatementNode()
public void parseStatementNode() {
// id对应于mapper接口的方法,单个mapper.xml中必须唯一,全路径名加+id作为mappedStatement的可以值,所以必须唯一
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");
Integer timeout = this.context.getIntAttribute("timeout");
String parameterMap = this.context.getStringAttribute("parameterMap");
String parameterType = this.context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = this.resolveClass(parameterType);
String resultMap = this.context.getStringAttribute("resultMap");
String resultType = this.context.getStringAttribute("resultType");
String lang = this.context.getStringAttribute("lang");
LanguageDriver langDriver = this.getLanguageDriver(lang);
Class<?> resultTypeClass = this.resolveClass(resultType);
String resultSetType = this.context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
String nodeName = this.context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = this.context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = this.context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", false);
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
includeParser.applyIncludes(this.context.getNode());
this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
String resultSets = this.context.getStringAttribute("resultSets");
String keyProperty = this.context.getStringAttribute("keyProperty");
String keyColumn = this.context.getStringAttribute("keyColumn");
String keyStatementId = id + "!selectKey";
keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
Object keyGenerator;
if (this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class>)
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
//1、解析节点,返回SqlSource
return builder.parseScriptNode();
}
public SqlSource parseScriptNode() {
MixedSqlNode rootSqlNode = this.parseDynamicTags(this.context);
SqlSource sqlSource = null;
if (this.isDynamic) {
sqlSource = new DynamicSqlSource(this.configuration, rootSqlNode);
} else {
//2、进入RawSqlSource
sqlSource = new RawSqlSource(this.configuration, rootSqlNode, this.parameterType);
}
return (SqlSource)sqlSource;
}
public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
Class<?> clazz = parameterType == null ? Object.class : parameterType;
//3、sql解析主要目的是将#{id}转换成?,这便到了大家熟悉的?问号参数占位符
this.sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap());
}
public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
SqlSourceBuilder.ParameterMappingTokenHandler handler = new SqlSourceBuilder.ParameterMappingTokenHandler(this.configuration, parameterType, additionalParameters);
GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
//4、真正替换是在这个方法中进行
String sql = parser.parse(originalSql);
return new StaticSqlSource(this.configuration, sql, handler.getParameterMappings());
}
public String parse(String text) {
if (text != null && !text.isEmpty()) {
int start = text.indexOf(this.openToken);
if (start == -1) {
return text;
} else {
char[] src = text.toCharArray();
int offset = 0;
StringBuilder builder = new StringBuilder();
for(StringBuilder expression = null; start > -1; start = text.indexOf(this.openToken, offset)) {
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(this.openToken);
offset = start + this.openToken.length();
} else {
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + this.openToken.length();
int end;
for(end = text.indexOf(this.closeToken, offset); end > -1; end = text.indexOf(this.closeToken, offset)) {
if (end <= offset || src[end - 1] != '\\') {
expression.append(src, offset, end - offset);
int var10000 = end + this.closeToken.length();
break;
}
expression.append(src, offset, end - offset - 1).append(this.closeToken);
offset = end + this.closeToken.length();
}
if (end == -1) {
builder.append(src, start, src.length - start);
offset = src.length;
} else {
//5、添加问号?占位符 builder.append(this.handler.handleToken(expression.toString()));
offset = end + this.closeToken.length();
}
}
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
} else {
return "";
}
}
最后会跑到这个方法里
public String handleToken(String content) {
Object parameter = this.context.getBindings().get("_parameter");
if (parameter == null) {
this.context.getBindings().put("value", (Object)null);
} else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
this.context.getBindings().put("value", parameter);
}
Object value = OgnlCache.getValue(content, this.context.getBindings());
// 5、获取?
String srtValue = value == null ? "" : String.valueOf(value);
this.checkInjection(srtValue);
return srtValue;
}
方法如下:
public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
this.mappedStatement.configuration = configuration;
this.mappedStatement.id = id;
this.mappedStatement.sqlSource = sqlSource;
this.mappedStatement.statementType = StatementType.PREPARED;
this.mappedStatement.resultSetType = ResultSetType.DEFAULT;
this.mappedStatement.parameterMap = (new org.apache.ibatis.mapping.ParameterMap.Builder(configuration, "defaultParameterMap", (Class)null, new ArrayList())).build();
this.mappedStatement.resultMaps = new ArrayList();
this.mappedStatement.sqlCommandType = sqlCommandType;
this.mappedStatement.keyGenerator = (KeyGenerator)(configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE);
String logId = id;
if (configuration.getLogPrefix() != null) {
logId = configuration.getLogPrefix() + id;
}
this.mappedStatement.statementLog = LogFactory.getLog(logId);
this.mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
}
我们又看到了Configuration类,这个贯穿mybatis框架始终,个人觉得Configuration它是mybatis的灵魂,几乎所有的信息他都持有,所有很多类也有这个类,这个也给我们一个启示,可以减轻框架设计的难度,想要什么资源就从这个类里找。
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 (this.unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
} else {
id = this.applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
org.apache.ibatis.mapping.MappedStatement.Builder statementBuilder = (new org.apache.ibatis.mapping.MappedStatement.Builder(this.configuration, id, sqlSource, sqlCommandType)).resource(this.resource).fetchSize(fetchSize).timeout(timeout).statementType(statementType).keyGenerator(keyGenerator).keyProperty(keyProperty).keyColumn(keyColumn).databaseId(databaseId).lang(lang).resultOrdered(resultOrdered).resultSets(resultSets).resultMaps(this.getStatementResultMaps(resultMap, resultType, id)).resultSetType(resultSetType).flushCacheRequired((Boolean)this.valueOrDefault(flushCache, !isSelect)).useCache((Boolean)this.valueOrDefault(useCache, isSelect)).cache(this.currentCache);
ParameterMap statementParameterMap = this.getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
//保存mappedStatement到configuration中的mappedStatements中
this.configuration.addMappedStatement(statement);
return statement;
}
}
这里id可以解释为什么每个mapper.xml的select|upadate|insert|delete节点要唯一的原因,namespace+id 作为key值保存在configuration标两种
到目前按位置mapper.xml已经解析完成了,那它如何和mapper接口绑定呢,请看
org.apache.ibatis.builder.xml.XMLMapperBuilder#parse方法中的this.bindMapperForNamespace();方法
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);
//1、添加绑定interface
this.configuration.addMapper(boundType);
}
}
}
MapperRegistry的绑定方法,如果该接口已经加载则不会第二次加载
并爆已经被注册异常:"Type " + type + " is already known to the MapperRegistry
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (this.hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
this.knownMappers.put(type, new MapperProxyFactory(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
this.knownMappers.remove(type);
}
}
}
}
到目前截止初始化mybatis,创建SqlSessionFactory类实例的已经完成。接下来是我们调用具体方法是如何执行sql呢。请看下节
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
executor的获取,默认采用SimpleExecutor执行器
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
DefaultSqlSession var8;
try {
Environment environment = this.configuration.getEnvironment();
TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
//1、事务级别,及开启与否
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//2、sql执行器
Executor executor = this.configuration.newExecutor(tx, execType);
var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
} catch (Exception var12) {
this.closeTransaction(tx);
throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12);
} finally {
ErrorContext.instance().reset();
}
return var8;
}
执行executor的getMapper方大获得接口对象实例 PaymentMapper paymentMapper = session.getMapper(PaymentMapper.class);
public <T> T getMapper(Class<T> type) {
//又是这个configuration进去看看如何获取的
return this.configuration.getMapper(type, this);
}
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
动态代理获取mapper接口实例
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
//1、动态代理获取接口实例
return this.newInstance(mapperProxy);
}
```java
protected T newInstance(MapperProxy<T> mapperProxy) {
//2、返回
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
调用具体方法
private MapperMethod cachedMapperMethod(Method method) {
return (MapperMethod)this.methodCache.computeIfAbsent(method, (k) -> {
return new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());
});
}
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
获得对应的方法和sql,中间其实还有几步,我省略了,大家自己调试的时候多看下
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName, Class<?> declaringClass, Configuration configuration) {
String statementId = mapperInterface.getName() + "." + methodName;
if (configuration.hasStatement(statementId)) {
return configuration.getMappedStatement(statementId);
} else if (mapperInterface.equals(declaringClass)) {
return null;
} else {
Class[] var6 = mapperInterface.getInterfaces();
int var7 = var6.length;
for(int var8 = 0; var8 < var7; ++var8) {
Class<?> superInterface = var6[var8];
if (declaringClass.isAssignableFrom(superInterface)) {
MappedStatement ms = this.resolveMappedStatement(superInterface, methodName, declaringClass, configuration);
if (ms != null) {
return ms;
}
}
}
return null;
}
}
}
获取到mappedStatement即根据接口路径和方法,找到了对应的sql语句,接下来就是执行sql语句的过程。
return mapperMethod.execute(this.sqlSession, args);
sql语句执行的地方:org.apache.ibatis.binding.MapperMethod#execute
public Object execute(SqlSession sqlSession, Object[] args)
获取参数
param = this.method.convertArgsToSqlCommandParam(args);
执行sql语句,及返回结果
result = sqlSession.selectOne(this.command.getName(), param);
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
进入sqlSession中执行sql语句
public <T> T selectOne(String statement, Object parameter) {
List<T> list = this.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;
}
}
执行查找,并开启加入缓存,mybatis缓存是默认开启的
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);
return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
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 (this.closed) {
throw new ExecutorException("Executor was closed.");
} else {
if (this.queryStack == 0 && ms.isFlushCacheRequired()) {
this.clearLocalCache();
}
List list;
try {
++this.queryStack;
list = resultHandler == null ? (List)this.localCache.getObject(key) : null;
if (list != null) {
this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
// 没有缓存则直接在数据库中查找,你以为这是最后的方法吗,不你错了,看接下来的方法
list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
--this.queryStack;
}
if (this.queryStack == 0) {
Iterator var8 = this.deferredLoads.iterator();
while(var8.hasNext()) {
BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)var8.next();
deferredLoad.load();
}
this.deferredLoads.clear();
if (this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
this.clearLocalCache();
}
}
return list;
}
}
// 没有缓存则直接在数据库中查找,你以为这是最后的方法吗,不你错了,看接下来的方法
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//1、存入缓存,用的是private Map
this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);
List list;
try {
list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
this.localCache.removeObject(key);
}
this.localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
this.localOutputParameterCache.putObject(key, parameter);
}
return list;
}
接着执行doQuery方法
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
//1、获取配置文件
Configuration configuration = ms.getConfiguration();
//2、获取PreparedStatementHandler
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//3、获取prepareStatement
stmt = this.prepareStatement(handler, ms.getStatementLog());
//4、执行
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement)statement;
//执行sql数据查找
ps.execute();
//结果处理
return this.resultSetHandler.handleResultSets(ps);
}
包装了n层,大家自己看的时候的耐心看,抽空画个时序图也许会直观很多。
整个流程就是这些,我只debug的是查找,大家有空可以把其余sql也试一下,本教程主要是给大家提供一个阅读源码的思路,不正确之处大家指正。认为有帮助可以尝试看看。
争取抽空画个时序图