分享好友 最新动态首页 最新动态分类 切换频道
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面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

最新文章
小欧工程师oppo专用root工具箱
编辑点评:小欧工程师oppo专用root工具箱小编今天给大家带来的是小欧工程师oppo专用root工具箱,它是专为oppo手机用户打造的,当然也只有玩机高手才能够弄懂它,OPPO手机用户只需找到应用管理,并卸载掉小欧工程师插件,然后一键root重新安
轻松生成超逼真美女写真:3款AI工具深度评测及使用技巧
限时免费,点击体验最近超火的AI生图神器,坐拥3000美女的大男主就是你! https://ai.sohu.com/pc/generate/textToImg?_trans_=030001_yljdaimn 在这个科技飞速发展的时代,人工智能不仅改变了我们的生活方式,还在各个领域展现出了惊人的
短剧市场新机遇:短视频小程序系统定制,集成CPS分销,源码搭建新趋势
**短剧市场新机遇:短视频小程序系统定制,集成CPS分销,源码搭建新趋势**随着互联网的快速发展,短剧市场正迎来新的机遇。短视频小程序系统的崛起,为短剧的传播和推广提供了新的平台。而将CPS分销模式与短视频小程序系统进行集成,不仅有
微信公众号如何推广
微信公众号如何推广  微信公众号率先提出标准的行业通用模板和深定制的微信平台开发理念相结合。小编收集了微信公众号如何推广相关内容,希望大家认真阅读!  一、互动沟通:  微信强调的互动不会是系统自动的人机互动,而是你与用户
《剑网3指尖江湖》高画质优化与设置攻略!
剑网3指尖江湖高画质攻略《剑网3指尖江湖》作为一款集结了江湖武侠与卡牌策略的手游,在画质表现方面一直有着不错的口碑。不过,部分玩家可能会遇到在高画质设置下,游戏运行不流畅、画面卡顿等问题。今天,我们将为大家提供一份详细的高画
音频剪辑制作
音频剪辑制作,一般又称剪音乐app。
迈威尔科技发布定制HBM解决方案:AI加速器的未来里程碑
2024年12月11日,迈威尔科技(MRVL.US)在其最新公告中宣布,其针对人工智能处理器的定制高带宽内存(HBM)解决方案取得显著突破。这一创新不仅提高了计算能力25%,内存容量增加33%,还提升了能效,标志着在AI加速器设计及实施领域的重要进
行业观察|医疗器械行业2024科创实力榜
今年两会上,加快培育新质生产力被列为政府今年的首要工作任务。所谓新质生产力就是创新起主导作用,摆脱传统经济增长方式、生产力发展路径,具有高科技、高效能、高质量特征,符合新发展理念的先进生产力质态。医疗器械作为现代医疗卫生体
查看linux中tcp连接数
一、查看哪些IP连接本机netstat -an二、查看TCP连接数1)统计80端口连接数netstat -nat|grep -i "80"|wc -l2)统计httpd协议连接数ps -ef|grep httpd|wc -l3)、统计已连接上的,状态为“establishednetstat -na|grep ESTABLISHED|wc -l4)、
厦门一站式网站建设服务,打造专业高效在线平台
在当今互联网时代,拥有一个专业的网站对于企业来说至关重要,厦门,这座美丽的海滨城市,同样需要一套全面、高效的网站建设服务来助力企业的发展,本文将为您详细介绍厦门全套网站建设的相关内容,帮助您了解如何打造一个专业、高效的在线
相关文章
推荐文章
发表评论
0评