iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >MyBatis的SqlSession.getMapper()源码分析
  • 557
分享到

MyBatis的SqlSession.getMapper()源码分析

2023-07-05 07:07:36 557人浏览 安东尼
摘要

今天小编给大家分享一下mybatis的sqlSession.getMapper()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来

今天小编给大家分享一下mybatissqlSession.getMapper()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

什么是 MyBatis?

MyBatis 是一款优秀的持久层框架

MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

原理解析

程序员和Mybatis 和数据的关系:人通过mybatis框架来操作数据库

思考问题并解决

问题1:首先我们必须告诉MyBatis要怎么操作数据库?

我们把可以通过XML配置文件或者注解的方式,MyBatis提供了一个类Configuration, Mybatis 读取XML配置文件后会将内容放在一个Configuration类中,Configuration类会存在整个Mybatis生命周期,以便重复读取。

问题2:想要Mybatis与数据库打交道,就要有一个类似于JDBC的Connection对象,在MyBatis中叫SqlSesion,所以我们要有一个SqlSession。

Mybatis 读取XML配置文件后会将内容放在一个Configuration类中,SqlSessionFactoryBuilder会读取Configuration类中信息创建SqlSessionFactory。SqlSessionFactory创建SqlSession。

String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);SqlSessionFactory  sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession=null;try{    sqlSession=sqlSessionFactory.openSession();    //some code    sqlSession.commit();} catch(Exception ex){    sqlSession.roolback();} finally{    if(sqlSession!=null){        sqlSession.close();    }}

关于SqlSessionFactory的创建,Mybatis采用构造模式来完成创建。

XMLConfigBuilder解析XML配置,读出配置参数,存入Configuration类中。

Configuration类创建SqlSessionFactory。(DefaultSqlSessionFactory的构造函数传入Configuration类)

深入了解:SqlSessionFactoryBuilder.builder(inputStream)

//该方法.builder中的主要内容:  XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);SqlSessionFactory localSqlSessionFactory = build(parser.parse()); //build(parser.parse())方法实则为: public SqlSessionFactory build(Configuration config) {   return new DefaultSqlSessionFactory(config); }

问题3:SqlSession能干什么?

SqlSession用途主要有两种

①. 获取对应的Mapper,让映射器通过命名空间和方法名称找到对应的SQL,发送给数据库执行后返回结果。

RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);Role role = roleMapper.getRole(1L);

②. 直接使用SqlSession,通过命名信息去执行SQL返回结果,该方式是IBatis版本留下的,SqlSession通过Update、Select、Insert、Delete等方法操作。

Role role = (Role)sqlSession.select("com.mybatis.mapper.RoleMapper.getRole",1L);

Mybatis底层利用jdk动态代理技术实现该接口,底层最后还是使用的IBatis中SqlSession通过Update、Select、Insert、Delete等方法操作。

问题4:上面说到Mybatis底层利用JDK动态代理技术实现该接口,但是我们在使用MyBatis的时候,都是只写接口不用写实现类,为什么呢?

为什么要使用动态代理?可以在不修改别代理对象代码的基础上,通过扩展代理类,进行一些功能的附加与增强。

我们先看看传统的JDK动态代理:

首先有一个接口

public interface Calculate {    void add(int i, int j);}

然后是接口的实现类

public class CalculateImp implements Calculate {    @Override    public void add(int i, int j) {        System.out.println("result = " + (i + j));    }}

代理类实现InvocationHandler

public class CalculateProxy implements InvocationHandler {    private Object target;    //总要让我知道要代理谁吧:构造方法中把传入一个代理类的实例    public CalculateProxy(Object target) {        this.target = target;    }    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        System.out.println("====== Before() ======");        method.invoke(target, args);        System.out.println("====== After () ======");        return null;    }}

拿到代理对象,操作接口方法

public class test {    public static void main(String[] args) {        InvocationHandler handler = new CalculateProxy(new CalculateImp());        Calculate calculateProxy =                (Calculate) Proxy.newProxyInstance(Calculate.class.getClassLoader(),                        new Class[]{Calculate.class},                        handler);        calculateProxy.add(10,20);    }}

Proxy.newProxyInstance()方法有三个参数:

类加载器(Class Loader)

需要实现的接口数组

InvocationHandler接口。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。这是动态代理的关键所在。

回到我们之前的问题,我们并没有接口实现类,那没有实现类还为什么还能调用方法操作。其实是这样的:

操作数据库主要是通过SQL语句,那么只要找到SQL语句然后执行不就可以!

通过例子分析:

BlogMapper mapper = session.getMapper(BlogMapper.class);Blog blog = mapper.selectBlog(1);

这里 mapper 可以调用selectBlog(1) 这个方法,说明 mapper 是个对象,因为对象才具有方法行为实现啊。BlogMapper接口是不能实例化的,更没有具体方法实现。

我们并没有定义一个类,让它实现BlogMapper接口,而在这里它只是通过调用session.getMapper() 所得到的。

由此,我们可以推断:肯定是session.getMapper() 方法内部产生了BlogMapper的实现类。有什么技术可以根据BlogMapper 接口生成了一个实现类呢?想到这里,对于有动态代理 。

Mapper 接口的注册

我们既然能够从SqlSession中得到BlogMapper接口的,那么我们肯定需要先在哪里把它放进去了,然后 SqlSession 才能生成我们想要的代理类啊。

我们可以从getMapper()联系,可能会有一个setMapper()或者addMapper()方法。确实是有!

configuration.addMapper(BlogMapper.class);

跟着这个 addMapper 方法的代码实现是这样的:

public <T> void addMapper(Class<T> type) {     mapperReGIStry.addMapper(type); }

我们看到这里 mapper 实际上被添加到 mapperRegistry (mapper注册器)中。继续跟进代码:

public class MapperRegistry {  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers                                                   = new HashMap<Class<?>, MapperProxyFactory<?>>();  public <T> void addMapper(Class<T> type) {    if (type.isInterface()) { // 只添加接口      if (hasMapper(type)) { // 不允许重复添加        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");      }      boolean loadCompleted = false;      try {        knownMappers.put(type, new MapperProxyFactory<T>(type)); // 注意这里         MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);        parser.parse();        loadCompleted = true;       } finally {        if (!loadCompleted) {          knownMappers.remove(type);        }      }    }  }}

我们首先看到MapperRegistry类,有一个私有属性knowMappers,它是一个HashMap 。

Key 为当前Class对象,value 为一个MapperProxyFactory实例

在MapperRegistry类的addMapper()方法中,knownMappers.put(type, new MapperProxyFactory<T>(type));相当于把:诸如BlogMapper之类的Mapper接口被添加到了MapperRegistry 中的一个HashMap中。

并以 Mapper 接口的 Class 对象作为 Key , 以一个携带Mapper接口作为属性的MapperProxyFactory实例作为value 。

MapperProxyFactory从名字来看,好像是一个工厂,用来创建Mapper Proxy的工厂。

上面我们已经知道,Mapper 接口被到注册到了MapperRegistry中&mdash;&mdash;放在其名为knowMappers 的HashMap属性中,我们在调用Mapper接口的方法的时候,是这样的:

BlogMapper mapper = session.getMapper(BlogMapper.class);

这里,我们跟踪一下session.getMapper() 方法的代码实现,这里 SqlSession 是一个接口,他有两个实现类,

一个是DefaultSqlSession,另外一个是SqlSessionManager,

这里我们用的是DefaultSqlSession. 为什么是DefaultSqlSession呢?因为我们在初始化SqlSessionFactory的时候所调用的SqlSessionFactoryBuilder的build()方法里边配置的就是DefaultSqlSession, 所以,我们进入到DefaultSession类中,看看它对session.getMapper(BlogMapper.class)是怎么实现的:

public class DefaultSqlSession implements SqlSession {  private Configuration configuration;        @Override  public <T> T getMapper(Class<T> type) {    return configuration.<T>getMapper(type, this); //最后会去调用MapperRegistry.getMapper  }}

如代码所示,这里的 getMapper 调用了 configuration.getMapper , 这一步操作其实最终是调用了MapperRegistry,而此前我们已经知道,MapperRegistry是存放了一个HashMap的,我们继续跟踪进去看看,那么这里的get,肯定是从这个hashMap中取数据。

我们来看看代码:

public class MapperRegistry {  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();// Mapper 映射  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {    final MapperProxyFactory<T> mapperProxyFactory =                                  (MapperProxyFactory<T>) knownMappers.get(type);        try {      return mapperProxyFactory.newInstance(sqlSession); // 重点看这里    } catch (Exception e) {    }  }}

我们调用的session.getMapper(BlogMapper.class);最终会到达上面这个方法,这个方法,根据BlogMapper的class对象,以它为keyknowMappers 中找到了对应的value &mdash;&mdash; MapperProxyFactory(BlogMapper) 对象,然后调用这个对象的newInstance()方法。

根据这个名字,我们就能猜到这个方法是创建了一个对象,代码是这样的:

public class MapperProxyFactory<T> { //映射器代理工厂   private final Class<T> mapperInterface;  private Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();   public MapperProxyFactory(Class<T> mapperInterface) {    this.mapperInterface = mapperInterface;  }  // 删除部分代码,便于阅读   @SuppressWarnings("unchecked")  protected T newInstance(MapperProxy<T> mapperProxy) {    //使用了JDK自带的动态代理生成映射器代理类的对象    return (T) Proxy.newProxyInstance(             mapperInterface.getClassLoader(),             new Class[] { mapperInterface },              mapperProxy);  }   public T newInstance(SqlSession sqlSession) {    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);    return newInstance(mapperProxy);  } }

看到这里,就清楚了,最终是通过Proxy.newProxyInstance产生了一个BlogMapper的代理对象。

Mybatis 为了完成 Mapper 接口的实现,运用了代理模式。

具体是使用了JDK动态代理,这个Proxy.newProxyInstance方法生成代理类的三个要素是:

  • ClassLoader &mdash;&mdash; 指定当前接口的加载器即可

  • 当前被代理的接口是什么 &mdash;&mdash; 这里就是 BlogMapper

  • 代理类是什么 &mdash;&mdash; 这里就是 MapperProxy

代理模式中,代理类(MapperProxy)中才真正的完成了方法调用的逻辑。

我们贴出MapperProxy的代码,如下:

public class MapperProxy<T> implements InvocationHandler, Serializable {// 实现了InvocationHandler    @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    //代理以后,所有Mapper的方法调用时,都会调用这个invoke方法       if (Object.class.equals(method.getDeclarinGClass())) {      try {        return method.invoke(this, args);  //  注意1      } catch (Throwable t) {        throw ExceptionUtil.unwrapThrowable(t);      }    }     final MapperMethod mapperMethod = cachedMapperMethod(method); // 使用了缓存    //执行CURD    return mapperMethod.execute(sqlSession, args); // 注意2  }       }

我们调用的 Blog blog = mapper.selectBlog(1); 实际上最后是会调用这个MapperProxyinvoke方法。

这段代码中,if 语句先判断,我们想要调用的方法是否来自Object类,这里的意思就是,如果我们调用toString()方法,那么是不需要做代理增强的,直接还调用原来的method.invoke()就行了。

只有调用selectBlog()之类的方法的时候,才执行增强的调用&mdash;&mdash;即mapperMethod.execute(sqlSession, args);这一句代码逻辑。

mapperMethod.execute(sqlSession, args);这句最终就会执行增删改查了,代码如下:

  public Object execute(SqlSession sqlSession, Object[] args) {    Object result;    if (SqlCommandType.INSERT == command.getType()) {         //insert  处理,调用SqlSession的insert      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.insert(command.getName(), param));    } else if (SqlCommandType.UPDATE == command.getType()) { // update      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.update(command.getName(), param));    } else if (SqlCommandType.DELETE == command.getType()) {   // delete      Object param = method.convertArgsToSqlCommandParam(args);      result = rowCountResult(sqlSession.delete(command.getName(), param));    } else if (SqlCommandType.SELECT == command.getType()) {      // 删除部分代码     } else {      throw new BindingException("Unknown execution method for: " + command.getName());    }     // 删除部分代码    return result;  }

再往下一层,就是执行JDBC那一套了,获取链接,执行,得到ResultSet,解析ResultSet映射成JavaBean。

以上就是“MyBatis的SqlSession.getMapper()源码分析”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

--结束END--

本文标题: MyBatis的SqlSession.getMapper()源码分析

本文链接: https://www.lsjlt.com/news/350430.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • MyBatis的SqlSession.getMapper()源码分析
    今天小编给大家分享一下MyBatis的SqlSession.getMapper()源码分析的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来...
    99+
    2023-07-05
  • MyBatis的通俗理解:SqlSession.getMapper()源码解读
    目录什么是 MyBatis?原理解析Mapper 接口的注册总结一下各个过程最后什么是 MyBatis? 直接看官方文档:https://mybatis.org/mybatis-3/...
    99+
    2023-03-01
    MyBatis SqlSession.getMapper() SqlSession.getMapper()源码 SqlSession.getMapper()
  • MyBatis底层源码分析
    🎄欢迎来到@边境矢梦°的csdn博文🎄 🎄本文主要梳理MyBatis底层源码分析 🎄 🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生ἰ...
    99+
    2023-10-20
    mybatis java 数据库
  • Mybatis Plus框架源码分析
    这篇文章主要介绍了Mybatis Plus框架源码分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Mybatis Plus框架源码分析文章都会有所收获,下面我们一起来看看吧。基础设计Bas...
    99+
    2023-07-05
  • Mybatis-Spring源码分析图解
    Mybatis-Spring 当我们使用mybatis和spring整合后为什么下面的代码可以运行? 一个问题: 我就写了个mapper接口为什么能用? 首先来看...
    99+
    2024-04-02
  • mybatis-plus查询源码的示例分析
    这篇文章主要介绍mybatis-plus查询源码的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!配置详情pom.xmldependency>     &...
    99+
    2023-06-29
  • Mybatis源码分析之插件模块
    Mybatis插件模块 插件这个东西一般用的比较少,就算用的多的插件也算是PageHelper分页插件; PageHelper官网:https://github.com/pagehe...
    99+
    2024-04-02
  • 【Mybatis源码解析】mapper实例化及执行流程源码分析
    文章目录 简介 环境搭建 源码解析 附 基础环境:JDK17、SpringBoot3.0、mysql5.7 储备知识:《【Spring6源码・AOP】AOP源码解析》、《JDBC详细...
    99+
    2023-08-20
    mybatis java spring boot
  • mybatis的executor包语句处理功能源码分析
    这篇“mybatis的executor包语句处理功能源码分析”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“mybatis的e...
    99+
    2023-06-29
  • MyBatis SqlSource源码示例解析
    目录正文SqlNodeSqlNode接口定义BoundSqlSqlSourceSqlSource解析时机SqlSource调用时机总结正文 MyBatis版本:3.5.12。 本篇...
    99+
    2023-02-13
    MyBatis SqlSource源码解析 MyBatis SqlSource
  • CesiumJS源码分析
    这篇文章主要介绍“CesiumJS源码分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“CesiumJS源码分析”文章能帮助大家解决问题。1. 有什么光CesiumJS 支持的光的类型比较少,默认场...
    99+
    2023-07-06
  • SocketServer 源码分析
    Creating network servers. contents SocketServer.py contents file head BaseServer BaseServer.serve_forever BaseServ...
    99+
    2023-01-31
    源码 SocketServer
  • RateLimiter 源码分析
    俗话说得好,缓存,限流和降级是系统的三把利剑。刚好项目中每天早上导出数据时因调订单接口频率过高,订单系统担心会对用户侧的使用造成影响,让我们对调用限速一下,所以就正好用上了。 常用的限流算法有2种:漏桶算法和令牌桶算法。漏桶算法漏...
    99+
    2023-05-31
    ratelimiter 源码 mi
  • YOLOv5的Backbone源码分析
    本篇内容主要讲解“YOLOv5的Backbone源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“YOLOv5的Backbone源码分析”吧!1 Backbone概览及参数# Pa...
    99+
    2023-06-30
  • Java源码分析:Guava之不可变集合ImmutableMap的源码分析
    目录一、案例场景二、ImmutableMap源码分析总结一、案例场景 遇到过这样的场景,在定义一个static修饰的Map时,使用了大量的put()方法赋值,就类似这样—— pu...
    99+
    2024-04-02
  • Mybatis源码解析之事务管理
    目录Mybatis事务管理和Spring整合后的事务管理Mybatis事务管理 我们可以在mybatis-config.xml中配置事务管理器的实现 <transactio...
    99+
    2024-04-02
  • ibatis源码与平台源码的示例分析
    这篇文章主要介绍了ibatis源码与平台源码的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。 原生类加...
    99+
    2024-04-02
  • MyBatis源码解析——获取SqlSessionFactory方式
    目录MyBatis源码解析_获取SqlSessionFactory首先从Resources.getResourceAsReader(path)进入到SqlSessionFactory...
    99+
    2024-04-02
  • Nebula Graph源码分析
    本篇内容介绍了“Nebula Graph源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!导读对于一些...
    99+
    2024-04-02
  • django源码分析 LazySetti
    一、django中通过LazySetting对象来获取项目的配置,LazySetting对象有什么特性?为什么使用这个对象? LazySetting顾名思义,就是延迟获取配置内容。比如,我们定义了一个对象A,并对其添加了一些属性,对A初始...
    99+
    2023-01-31
    源码 django LazySetti
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作