首页 > 其他分享 >【简写MyBatis】01-简单映射器

【简写MyBatis】01-简单映射器

时间:2024-02-16 19:22:20浏览次数:21  
标签:Class 01 映射器 接口 mapperInterface sqlSession MyBatis public

前言

新开一个坑,为了学习一下MyBatis的源码,写代码是次要的,主要为了吸收一下其中的思想和手法。

目的

关联对象接口和映射类的问题,把 DAO 接口使用代理类,包装映射操作。

知识点

  1. 动态代理
  2. 简单工厂模式
  3. InvocationHandler接口的使用

实现

既然是简易的MyBatis编写,那肯定得看下源码了;先来一波回忆,MyBatis的使用:

忘记的朋友可以看下之前写的MyBatis手册: https://blog.csdn.net/weixin_43908900/article/details/129780085

https://www.cnblogs.com/xbhog/p/17258782.html

@Test
public void testMybatis(){
    //加载核心配置文件
    try {
        //字符流加载配置
        InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
        //创建sql连接工厂
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
        //创建session连接,设置true,默认提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(true);
        //反射获取类对象
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userAll = mapper.getUserAll();
        System.out.println(userAll);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

代码中可以看到,接口和映射器有关系的地方应该是sqlSession.getMapper(UserMapper.class);点进去。

先看映射器工厂类:MapperProxyFactory

这部分就是我们本次实现的地方。

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();

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

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethodInvoker> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  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);
  }

}

简易映射器类图:

MapperProxy代理类来代理需要使用的接口,为了方便后续的维护扩展,在代理类上加一层代理工厂类MapperProxyFactory

使用代理类的好处是: 动态代理允许 MyBatis 在不修改接口实现的情况下,为接口方法提供自定义的行为。这意味着开发者只需要定义接口和 SQL 映射,而无需编写接口的实现类。这种设计促进了关注点分离,使得数据访问逻辑(SQL)与业务逻辑更加清晰,MapperProxy 能够在运行时将 SQL 语句与接口方法动态绑定,这样,MyBatis 可以根据接口方法的签名和注解或 XML 配置来执行相应的 SQL 操作。

使用简单工厂模式的好处: 实现代码复用和模块化,对代理逻辑和接口使用解耦,灵活性高,不改变公共接口等。

总之都是为了项目的高度灵活、扩展、复用等。

通过上述的分析,现在进行代码编写的流程比较明朗了。

代理类的实现:

public class MapperProxy<T> implements InvocationHandler, Serializable {

    private static final long serialVersionUID = -6424540398559729838L;
    //模拟SqlSession
    private Map<String, String> sqlSession;
    private final Class<T> mapperInterface;

    public MapperProxy(Map<String, String> sqlSession, Class<T> mapperInterface) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //检查一个方法是否来自特定的类或者是一系列接口中的一个
        //是Object自身的方法,就没必要代理,直接调用就行
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        } else {
            //需要匹配的是类名+方法名
            return "你的被代理了!" + sqlSession.get(mapperInterface.getName() + "." + method.getName());
        }
    }

}

映射器代理工厂实现:

public class MapperProxyFactory<T> {

    private final Class<T> mapperInterface;

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

    public T newInstance(Map<String, String> sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface);
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }

}

执行流程如下:

测试

public void testApp() {
    MapperProxyFactory<IUserDao> proxyFactory = new MapperProxyFactory<>(IUserDao.class);
    Map<String,String> sqlSession = new HashMap<>();
    sqlSession.put("com.xbhog.IUserDao.getUserName","模拟执行 Mapper.xml 中 SQL 语句的操作:查询用户姓名");
    IUserDao userDao = proxyFactory.newInstance(sqlSession);
    String userName = userDao.getUserName("100001");
    System.out.println(userName);
}

总结

  1. 通过追溯MyBatis中的源码,明确本文的主要的内容
  2. 明确目标类的依赖关系
  3. 代码实现简易效果
  4. 明确执行流程
  5. 测试代码,符合预期结果

参考&学习

https://mp.weixin.qq.com/s/G3fZES2FvNQK8JLnd9Hx9w

AI大模型辅助

MyBatis源码

标签:Class,01,映射器,接口,mapperInterface,sqlSession,MyBatis,public
From: https://www.cnblogs.com/xbhog/p/18017397

相关文章

  • 01trie
    01trie定义01-trie是字符集为0,1的trie,可以维护异或极值,维护异或和实现主体仍然是trie,维持\(t\)数组记录儿子不变。需要因为异或的性质,所以只需要维护加入0/1边的奇偶性即可,所以添加\(w\)数组记录父节点到该节点的边数。此外因为要统计异或和,所以要在树上统计,用\(xorv......
  • mybatis plus基础
    mybatisplus1引入依赖<!--MybatisPlus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version></dependency>2创建mapper,继......
  • 题解 LGP10144【[WC/CTS2024] 水镜】
    题解P10144【[WC/CTS2024]水镜】题目描述给定一个长度为\(n\)的正整数序列\(h_1,h_2,\cdots,h_n\),求满足以下所有条件的二元组\((u,v)\)的数量:\(1\leu<v\len\),且\(u,v\)为整数;存在一个正实数\(L\)以及一个长度为\((v-u+1)\)的序列\(r_u,r_{u+......
  • 研究生阶段 2018.11.1 编程 我的微信小程序
    微信小程序昵称:HelloPrince2017原始ID:gh_5c258db11408登录邮箱:[email protected]你好,以上帐号未在指定时间内登录,此帐号已冻结,如需重新使用此帐号,请登录小程序帐号后台进行找回;或在公众平台找回帐号流程中,通过原始ID搜索找回  "找回小程序登录密码"  发......
  • MyBatis-Plus--在xml中使用wrapper的方法
    原文网址:​​MyBatis-Plus--在xml中使用wrapper的方法_IT利刃出鞘的博客-CSDN博客​​简介本文介绍MyBatis-Plus如何在xml中使用wrapper。分享Java技术星球(自学精灵):​​https://learn.skyofit.com​​ServiceQueryWrapper<T>wrapper=newQueryWrapper<T>();wrapper.eq("......
  • P3643 [APIO2016] 划艇
    题意给定数列\(a,b\),试求出序列\(S\)的方案数,使得:\(a_i\leS_i\leb_i,S_{i-1}<S_i\)或\(S_i=0\)。\(S\)不能是全\(0\)序列。\(a_i,b_i\le10^9,n\le500\)Sol不难想到一个trivial的思路。设\(f_{i,j}\)表示确定了\(S_1\toS_i\),并且\(S......
  • AtCoder Grand Contest 012 E Camel and Oases
    洛谷传送门AtCoder传送门容易发现跳跃次数为\(O(\logV)\)。考虑对于跳跃\(k\)次后的限制\(\left\lfloor\frac{V}{2^k}\right\rfloor\),对每个点预处理出不再跳跃能到达的最左和最右的点\([l_{k,i},r_{k,i}]\)。于是问题变成了,从第\(i\)个区间集选择一个区间\([a_i,......
  • P3958 [NOIP2017 提高组] 奶酪
    原题链接思路并查集然后看看是否存在上表面联通的洞与下表面联通的洞位于同一集合code#include<bits/stdc++.h>usingnamespacestd;doublen,h,r;intfa[1005];vector<int>up,down;struct{doublex,y,z;}hole[1005];doubledis(inti,intj){returnpo......
  • P1873 [COCI 2011/2012 #5] EKO / 砍树
    题目链接:一、本题为什么能想到利用二分解决?\(1.\)有单调性提高伐木机的高度,显然地,得到的木头会减少。同样地,放低得到的木头会增多。而正因为答案有单调性,所以我们可以使用二分。\(2.\)数据范围大如果采用暴力枚举,时间复杂度为\(O(n\cdotm)\)会超时。用二分优化后时间......
  • 01 \| 网络互联的昨天、今天和明天:HTTP 协议的演化
    作者:四火你好,我是四火。HTTP协议是互联网基础中的基础,和很多技术谈具体应用场景不同的是,几乎所有的互联网服务都是它的应用,没有它,互联网的“互联”将无从谈起,因此我们把它作为正式学习的开篇。说到其原理和协议本身,我相信大多数人都能说出个大概来,比如,有哪些常见的方法,常......