MyBatis是如何让我们通过接口就能调用到SQL的

简介: MyBatis是如何让我们通过接口就能调用到SQL的

大致可分为如下几个步骤

1. 动态注册bean

1.1 根据配置mapperScan, 扫描对应的包, 将对应的类解析成BeanDefinition
1.2 通过替换BeanDefinition中的BeanClass为MapperFactoryBean, (原来的BeanClass是Mapper接口) 实现了在spring生成对应的对象时,  返回的对象不是本身类型的对象,而是MapperFactoryBean重写FactoryBean接口的getObject()方法返回的代理对象。该方法getObject()已经对mapper接口进行了代理,  即后续进行自动注入时, 也是返回getObject()生成的代理对象

2. 生成对应的代理对象

2.1 在getObject()方法中, 会获取到接口的全限定名称, 然后进一步对代理方法进行封装, 调用链如下
MapperFactoryBean: 
        public T getObject() throws Exception {
          return getSqlSession().getMapper(this.mapperInterface);
        }
       DefaultSqlSession: 
           public <T> T getMapper(Class<T> type) {
            //configuration是mybatis的重要配置类, 在初始化的时候, 就会将mapper接口添加到configuration中
            return configuration.getMapper(type, this);
           }
       Configuration: 
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
          return mapperRegistry.getMapper(type, sqlSession);
        }
       MapperRegistry: 
        public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
            //获取mapper代理工厂
          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: 
        protected T newInstance(MapperProxy<T> mapperProxy) {
          return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
        }
        public T newInstance(SqlSession sqlSession) {
          //返回一个新代理对象
          final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
          return newInstance(mapperProxy);
        }


2.2 在org.apache.ibatis.binding.MapperProxy#cachedInvoker中, new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())), 并且将代理方法调用器缓存起来. MapperMethod该对象即是最终调用方法的对象.

3. 执行对应的方法

3.1 方法调用, 代理里最常见的方法invoke
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
          //过滤掉object的方法
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
          } else {
            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      }


3.2. org.apache.ibatis.binding.MapperProxy.PlainMethodInvoker重写的invoke方法中,判断该方法是否被调用过(是否存在于缓存), 若没有, 则创建一个PlainMethodInvoker方法调用器, 传入MapperMethod(MapperMethod是真正执行方法的对象), 并将新创建的PlainMethodInvoker存入缓存中(methodCache), 并调用该PlainMethodInvoker的invoke方法,
3.3. xml中的id和select等标签封装成了SqlCommand, 调用mapperMethod的execute, 执行对应的增删改查.

4. 结果集封装, 进行一些数据库数据对应java对象的转换

通过mybatis的封装和代理, 将mapper.xml转换成了接口的实例对象

如有谬误, 欢迎斧正

简化版如下: https://blog.csdn.net/sinat_25991865/article/details/89891581

public interface UserMapper {
  List<SysUser> selectAll();
}


public class MyMapperProxy<T> implements InvocationHandler {
  private Class<T> mapperInterface;
  private SqlSession sqlSession;
  public MyMapperProxy(Class<T> mapperInterface, SqlSession sqlSession) {
      this.mapperInterface = mapperInterface;
      this.sqlSession = sqlSession;
  }
  @Override
  public Object invoke(Object proxy , Method method , Object[] args)
      throws Throwable {
    //针对不同的 sql 类型,需要调用sqlSession不同的方法
    //接口方法中的参数也有很多情况 ,这里只考虑没有有参数的情况
    List<T> list= sqlSession.selectList(
        mapperInterface.getCanonicalName() + ”.” + method.getName());
    //返回数据也有很多情况,这里不做处理直接返回
    return list;
  }
}


方法调用

//获取sqlSession
SqlSession sqlSession = getSqlSession();
//获取 UserMapper 接口
MyMapperProxy userMapperProxy = new MyMapperProxy(
    UserMapper.class , sqlSession) ;
UserMapper userMapper = (UserMapper) Proxy.newProxyinstance (
    Thread.currentThread().getContextClassLoader(),
    new Class[ ] {UserMapper.class},
    userMapperProxy) ;
//调 用 selectAll 方 法
List<SysUser> user= userMapper.selectAll();


目录
相关文章
|
8天前
|
SQL XML Java
mybatis实现动态sql
MyBatis的动态SQL功能为开发人员提供了强大的工具来应对复杂的查询需求。通过使用 `<if>`、`<choose>`、`<foreach>`等标签,可以根据不同的条件动态生成SQL语句,从而提高代码的灵活性和可维护性。本文详细介绍了动态SQL的基本用法和实际应用示例,希望对您在实际项目中使用MyBatis有所帮助。
28 11
|
1月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
29天前
|
SQL Java 数据库连接
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
canal-starter 监听解析 storeValue 不一样,同样的sql 一个在mybatis执行 一个在数据库操作,导致解析不出正确对象
|
2月前
|
SQL Java 数据库连接
mybatis使用四:dao接口参数与mapper 接口中SQL的对应和对应方式的总结,MyBatis的parameterType传入参数类型
这篇文章是关于MyBatis中DAO接口参数与Mapper接口中SQL的对应关系,以及如何使用parameterType传入参数类型的详细总结。
54 10
|
3月前
|
SQL XML Java
mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法
文章介绍了MyBatis中动态SQL的用法,包括if、choose、where、set和trim标签,以及foreach标签的详细使用。通过实际代码示例,展示了如何根据条件动态构建查询、更新和批量插入操作的SQL语句。
mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法
|
3月前
|
SQL XML Java
mybatis :sqlmapconfig.xml配置 ++++Mapper XML 文件(sql/insert/delete/update/select)(增删改查)用法
当然,这些仅是MyBatis功能的初步介绍。MyBatis还提供了高级特性,如动态SQL、类型处理器、插件等,可以进一步提供对数据库交互的强大支持和灵活性。希望上述内容对您理解MyBatis的基本操作有所帮助。在实际使用中,您可能还需要根据具体的业务要求调整和优化SQL语句和配置。
70 1
|
4月前
|
SQL 安全 Java
访问者模式问题之在上面的 SQL 结构定义中, sealed 接口的作用如何理解
访问者模式问题之在上面的 SQL 结构定义中, sealed 接口的作用如何理解
|
3月前
|
关系型数据库 MySQL 网络安全
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
|
5月前
|
SQL 存储 监控
SQL Server的并行实施如何优化?
【7月更文挑战第23天】SQL Server的并行实施如何优化?
135 13
|
5月前
|
SQL
解锁 SQL Server 2022的时间序列数据功能
【7月更文挑战第14天】要解锁SQL Server 2022的时间序列数据功能,可使用`generate_series`函数生成整数序列,例如:`SELECT value FROM generate_series(1, 10)。此外,`date_bucket`函数能按指定间隔(如周)对日期时间值分组,这些工具结合窗口函数和其他时间日期函数,能高效处理和分析时间序列数据。更多信息请参考官方文档和技术资料。