• Mybatis Mapper接口和xml绑定的多种方式、内部实现原理和过程


    一、绑定方式

    1. XML文件方式

    Mybatis中,我们需要创建一个与实体类对应的Mapper接口,然后在该接口上添加方法,这些方法对应着SQL语句。然后,我们需要创建一个XML文件,这个文件中包含了SQL语句和映射关系。

    例如,我们有一个User实体类和一个UserMapper接口:

    public interface UserMapper {
        User getUserById(int id);
    }
    
    • 1
    • 2
    • 3

    然后,我们可以创建一个名为UserMapper.xml的文件,内容如下:

    <mapper namespace="com.example.dao.UserMapper">
        <select id="getUserById" resultType="com.example.entity.User">
            SELECT * FROM user WHERE id = #{id}
        </select>
    </mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在这个XML文件中,namespace属性指定了Mapper接口的全限定名,id属性指定了SQL语句的唯一标识符,resultType属性指定了查询结果的类型。

    2. 注解方式

    Mybatis也支持通过注解的方式来进行映射。首先,需要在Mapper接口上添加@Mapper注解,然后在方法上添加@Select、@Insert、@Update、@Delete等注解来指定SQL语句。

    例如,我们可以将上面的UserMapper接口改为注解方式:

    import org.apache.ibatis.annotations.*;
    
    @Mapper
    public interface UserMapper {
        @Select("SELECT * FROM user WHERE id = #{id}")
        User getUserById(int id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这个例子中,@Mapper注解表示这是一个Mapper接口,@Select注解表示这是一个查询语句,#{id}是参数占位符。

    3. 需要注意

    启动类上添加@MapperScan注解指定扫描路径

    @SpringBootApplication
    @MapperScan("com.example.mapper") // 指定扫描路径
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    不用@MapperScan注解,也可以在mybatis-config.xml中配置mapper映射文件的位置和命名空间

    <configuration>
        <mappers>
            <mapper resource="com/example/mapper/UserMapper.xml"/> // 指定Mapper映射文件的位置和名称
        </mappers>
    </configuration>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、实现原理

    1. 原理

    Mybatis的Mapper接口和xml绑定的原理主要依赖于JDK动态代理技术

    而Mybatis中MapperProxy代理类是mybatis实现Mapper接口和xml绑定的核心类之一。它实现了InvocationHandler接口,用于拦截Mapper接口方法的调用,并将方法名和参数传递给SqlSession对象执行相应的SQL语句。

    2. MapperProxy代理类的实现过程

    1. 首先,通过JDK动态代理技术生成一个MapperProxy代理类实例。这个代理类实现了Mapper接口,并重写了接口中的方法。
    2. 在重写的方法中,MapperProxy会拦截方法调用,并将方法名和参数传递给SqlSession对象执行相应的SQL语句。
    3. SqlSession对象会根据Mapper接口的namespace值找到对应的mapper.xml文件,并根据id值找到对应的SQL语句。然后,根据返回值类型和参数类型等信息,生成相应的Java代码。这些Java代码会包含对SqlSession的操作,例如查询、更新等操作。最终,SqlSession对象会将这些Java代码编译成字节码,并加载到JVM中运行。
    4. 当SQL语句执行完毕后,SqlSession对象会将结果返回给MapperProxy代理类。然后,MapperProxy代理类会将结果映射为Java对象,并返回给调用者。

    3. MapperProxy代理类源码

    下面是MapperProxy代理class的核心方法实现:mybatis3.5.9

    public class MapperProxy<T> implements InvocationHandler, Serializable {
    
      @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 {
            return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      }
    
      private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
        try {
          return MapUtil.computeIfAbsent(methodCache, method, m -> {
            if (m.isDefault()) {
              try {
                if (privateLookupInMethod == null) {
                  return new DefaultMethodInvoker(getMethodHandleJava8(method));
                } else {
                  return new DefaultMethodInvoker(getMethodHandleJava9(method));
                }
              } catch (IllegalAccessException | InstantiationException | InvocationTargetException
                  | NoSuchMethodException e) {
                throw new RuntimeException(e);
              }
            } else {
              return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
            }
          });
        } catch (RuntimeException re) {
          Throwable cause = re.getCause();
          throw cause == null ? re : cause;
        }
      }
    
      private MethodHandle getMethodHandleJava9(Method method)
          throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        final Class<?> declaringClass = method.getDeclaringClass();
        return ((Lookup) privateLookupInMethod.invoke(null, declaringClass, MethodHandles.lookup())).findSpecial(
            declaringClass, method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()),
            declaringClass);
      }
    
      private MethodHandle getMethodHandleJava8(Method method)
          throws IllegalAccessException, InstantiationException, InvocationTargetException {
        final Class<?> declaringClass = method.getDeclaringClass();
        return lookupConstructor.newInstance(declaringClass, ALLOWED_MODES).unreflectSpecial(method, declaringClass);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
  • 相关阅读:
    01-为什么 switch case 语句需要加入 break
    最新一站式AI创作中文系统网站源码+系统部署+支持GPT对话、Midjourney绘画、Suno音乐、GPT-4o文档分析等大模型
    【无标题】
    【Qt】Qt再学习(十七):QThread、QMutex、QTimer
    【网络安全的神秘世界】XSS基本概念和原理介绍
    C#版本LINQ增强开源库
    面试必备:聊聊分布式锁的多种实现!
    可见光相机曝光方式
    K-近邻算法的 sklearn 实现
    springboot+高校教室排课系统 毕业设计-附源码221556
  • 原文地址:https://blog.csdn.net/weixin_42170236/article/details/134391559