分享好友 最新动态首页 最新动态分类 切换频道
2024年MyBatis-底层源码解析-(详细),阿里技术专家深入讲解
2024-12-26 23:18

架构学习资料

由于篇幅限制小编,pdf文档的详解资料太全面,细节内容实在太多啦,所以只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取


throw new BuilderException(“A mapper element may only specify a url, resource or class, but not more than one.”);
}
}
}
}
}
}

我的UserMapper.xml文件内容为

<?xml version="1.0" encoding="UTF-8"?> select * from `user` where userId = #{userId}

UserMapper接口内容为

package com.mapper;

import com.entity.User;
import org.apache.ibatis.annotations.Select;

public interface UserMapper {
@Select(“select * from where userId = 2”)
User findById(int userId);
}

疑问?UserMapper.xml有<select id=“findById”,而在接口中的findById方法我又加了一个@Select注解;那么执行会选择哪一条Sql执行还是报错呢

public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
if (!configuration.isResourceLoaded(resource)) {

bindMapperForNamespace();
}

parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
}

解析 mapper.xml文件内容

public class XMLMapperBuilder extends BaseBuilder {
private void configurationElement(XNode context) {
try {

buildStatementFromContext(context.evalNodes(“select|insert|update|delete”));
} catch (Exception e) {
throw new BuilderException(“Error parsing Mapper XML. The XML location is '” + resource + "'. Cause: " + e, e);
}
}
}

builderAssistant.addMappedStatement,并不是添加一个mapper.xml文件隐射的实例而是为每一个Sql语句创建一个实例

public class XMLStatementBuilder extends BaseBuilder {
private final MapperBuilderAssistant builderAssistant;
public void parseStatementNode() {

builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}

创建一个MappedStatement实例添加到 Configuration.mappedStatements的Map集合中

public class XMLStatementBuilder extends BaseBuilder {
public MappedStatement addMappedStatement() {


ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}


MappedStatement statement = statementBuilder.build();


configuration.addMappedStatement(statement);
return statement;
}
}

开始解析接口注解,并添加一个MapperProxyFactory代理工厂的对象到configuration.mapperRegistry.knownMappers;key是Mapper接口

public class XMLMapperBuilder extends BaseBuilder {
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {

configuration.addMapper(boundType);
}
}
}
}
}

public class Configuration {
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public void addMapper(Class type) {

mapperRegistry.addMapper(type);
}
}

mapperRegistry.addMapper(type)
为Mapper接口创建一个代理工厂,方便后期使用Mapper接口时创建代理类

解析mapper接口的注解信息

public class MapperRegistry {
public void addMapper(Class type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException(“Type " + type + " is already known to the MapperRegistry.”);
}
boolean loadCompleted = false;
try {

parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
}

解析mapper接口的注解信息,parseStatement(method)主要在这个方法中完成

public class MapperAnnotationBuilder {
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());

parsePendingMethods();
}
}

创建一个MappedStatement实例添加到 Configuration.mappedStatements的Map集合中

public class MapperAnnotationBuilder {
private final MapperBuilderAssistant assistant;
void parseStatement(Method method) {

assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO gcode issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
options != null ? nullOrEmpty(options.resultSets()) : null);
}
}

这里重点分析Configuration.addMappedStatement(statement);在做什么操作,并且解决 1.4.2留下的疑点;UserMapper.xml和UserMapper接口都有findById的Sql语句定义

public class Configuration {
public void addMappedStatement(MappedStatement ms) {
mappedStatements.put(ms.getId(), ms);
}
}

mappedStatements.put(ms.getId(), ms); 实际调用 Configuration.StrictMap.put()方法
Configuration.StrictMap是一个重写的HashMap,put方法会先校验key是否存在

public class Configuration {

protected final Map<String, MappedStatement> mappedStatements = new Configuration.StrictMap(“Mapped Statements collection”)
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());

protected static class StrictMap extends HashMap<String, V> {
@Override
@SuppressWarnings(“unchecked”)
public V put(String key, V value) {

if (containsKey(key)) {
throw new IllegalArgumentException(name + " already contains value for " + key

  • (conflictMessageProducer == null ? “” : conflictMessageProducer.apply(super.get(key), value)));
    }
    if (key.contains(“.”)) {
    final String shortKey = getShortName(key);
    if (super.get(shortKey) == null) {
    super.put(shortKey, value);
    } else {
    super.put(shortKey, (V) new org.apache.ibatis.session.Configuration.StrictMap.Ambiguity(shortKey));
    }
    }
    return super.put(key, value);
    }
    }
    }

public class Main {
public static void main(String[] args) throws IOException {
String configName = “mybatis_config.xml”;
Reader reader = Resources.getResourceAsReader(configName);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);


SqlSession sqlSession = sqlSessionFactory.openSession();


UserMapper userMapper = sqlSession.getMapper(UserMapper.class);


User user = userMapper.findById(1);
System.out.println(user);
}
}

输出结果:User{userId=1, username=‘张三’, sex=‘男’, age=12}

一行代码的查询,底层既然走了那么多流程

调用DefaultSqlSessionFactory.openSessionFromDataSource();

public class DefaultSqlSessionFactory implements SqlSessionFactory {
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {

final Environment environment = configuration.getEnvironment();


final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);


tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);


final Executor executor = configuration.newExecutor(tx, execType);


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();
}
}
}

public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {

if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
}

获取Mapper接口代理类实例

public class DefaultSqlSession implements SqlSession {
@Override
public T getMapper(Class type) {

return configuration.getMapper(type, this);
}
}

public class Configuration {
public T getMapper(Class type, SqlSession sqlSession) {

return mapperRegistry.getMapper(type, sqlSession);
}
}

public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

public T getMapper(Class type, SqlSession sqlSession) {

return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}

InvocationHandler是MapperProxy

public class MapperProxyFactory {

private final Class mapperInterface;

private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}

@SuppressWarnings(“unchecked”)
protected T newInstance(MapperProxy mapperProxy) {

return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {

final MapperProxy mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}

会走代理类 MapperProxy.invoke

public class MapperProxy implements InvocationHandler, Serializable {

private static final long serialVersionUID = -6424540398559729838L;
private final SqlSession sqlSession;
private final Class mapperInterface;
private final Map<Method, MapperMethod> methodCache;

public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map<Method, MapperMethod> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (method.isDefault()) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}

return mapperMethod.execute(sqlSession, args);
}


private MapperMethod cachedMapperMethod(Method method) {
return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
}

执行Sql语句查询,由于我的返回结果是一个 User对象,所以会走到
result = sqlSession.selectOne(command.getName(), param);这一行查询一条记录

实际走到 DefaultSqlSession.selectOne()

public class MapperMethod {

private final org.apache.ibatis.binding.MapperMethod.SqlCommand command;
private final org.apache.ibatis.binding.MapperMethod.MethodSignature method;

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new org.apache.ibatis.binding.MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new org.apache.ibatis.binding.MapperMethod.MethodSignature(config, mapperInterface, method);
}

result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
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 class DefaultSqlSession implements SqlSession {

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();
}
}
}

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

dStatement(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();
}
}
}

码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到

又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考

以下是部分内容截图
[外链图片转存中…(img-ODVSBlH2-1715002820323)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

最新文章
微信推广引流怎么做(4个精准引流的大招)
在这里首先要说的是,现在已经过了暴力吸粉的时候了,很多人通过各种方法引流来不精准的粉丝,无论对后期运营还是变现都产生了很大影响。因为引流不精准,用户对你需求度不高,自然不是拉黑就是选择性忽略,所以精准引流要先找准对标用户的
现代警务探索|兰州安宁公安AI战队:聚“数”成塔 乘“智”而上
“团队成员来自情指、治安、网安、特警、派出所等多个单位。” “AI战队”负责民警魏一茜十分自信:“别看他们来自不同的单位,实际上个个都是建模能手!”今年4月,安宁分局成功举办第一届大数据建模比武竞赛,展示出的跳跃思维和涌现出的
高清美女写真:用搜狐简单AI一键生成梦幻女友!
限时免费,点击体验最近超火的AI生图神器,坐拥3000美女的大男主就是你! https://ai.sohu.com/pc/generate/textToImg?_trans_=030001_yljdaimn 在这个以视觉为王的时代,越来越多的年轻人喜欢通过人工智能来创作与分享自己的美丽幻想。想
百度输入法 V6.1.13.6
百度输入法是百度公司推出的一款非常酷的输入法,它打字速度快、使用顺畅,还有各种皮肤和颜文字词库,让你的输入法个性潮流。百度输入法不仅有强大的词库还有亲笔信、神码传文、百度日历、百度袋鼠等特色功能,而且百度输入法非常注重用户
环幕影院
苏州图腾机柜有限公司_网络服务器机柜|工业控制柜|配电柜|高低压...苏州图腾机柜有限公司位于江苏苏州高新技术开发区,是深圳图腾总部授权专业销售图腾网络服务器机柜,工业控制柜,配电柜,高低压开关柜,CB电子箱等电气化产品公司,公司
网站制作费用多少合适?小白必看的超实用费用指南
  “做网站会不会很贵啊?”“一万块够不够?几百块行不行?”关于网站制作费用,这些问题估计在不少新手的脑海里盘旋过吧!实际上,网站制作的成本是一个综合考量内容、设计和功能的过程,没有标准答案,但绝对有规律可循。本文结合真实案例
苹果手机助手有哪些?苹果手机用什么助手最好?
  你知道苹果手机助手有哪些?苹果手机用什么助手最好?iOS手机助手哪个好用吗?  现在的苹果手机助手越来越强大,海量游戏应用和软件可随意让用户免费下载。大到几十上百元,小到付费测试版,以及还没有正式上线的所有应用,只需一键
谷歌深夜发布Gemini 2.0,人工智能(159819)、软件30ETF(562930)备受关注
  消息面上,12月11日晚,刚刚发布量子芯片的谷歌,又投下一枚重磅炸弹,正式发布其最新版大模型Gemini 2.0系列,谷歌表示这是他们迄今为止最强大的人工智能模型,“专为智能体时代设计”,并同时介绍了多个智能体应用(AI Agent)。  
航海王鱼人岛篇特别编辑版
影片名称:航海王鱼人岛篇特别编辑版影片别名:影片类型:动漫影片导演:尾田荣一郎影片演员:田中真弓 , 冈村明美 , 中井和哉 , 山口胜平 , 平田广明 , 大谷育江 , 山口由里子 , 矢尾一树 , 长岛雄一 , 宝龟克寿年份地区:2024/日本更新时
规则引擎 drools_Java常用的规则引擎,让你变动的代码更加容易维护
在本文中,我们将介绍Java中一些最受欢迎的规则引擎。规则引擎由推理引擎发展而来,是一种嵌入在应用程序中的组件,实现了将业务决策从应用程序代码中分离出来,并使用预定义的语义模块编写业务决策。接受数据输入
相关文章
推荐文章
发表评论
0评