2 Star 5 Fork 0

刘超群 / MyBatisDemo

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

MybatisPlus 源码很难?要不我先带你实现一个简单的 ORM 框架你再去看呢?

先介绍一下如何使用我这个项目

:+1: 1.下载代码

:v: 2.本地 maven install

:muscle: 3.添加依赖到任意的一个 springboot 项目中

  <groupId>com.orm</groupId>
  <artifactId>MyBatisDemo</artifactId>
  <version>1.1-SNAPSHOT</version>

:heart: 4.在你的项目中启动类上加注解 @EnableMyBatis

:tired_face: 5.在写 mapper 的时候继承我指定的类并泛型为实体类

   // 你的实体类应该是这样的 
        @TableName("t_user")
        @Data
        public class User {
            @FieldName("id")
            private String id;
            @FieldName("name")
            private String username;
        
        }

 // 比如这样

 public interface UserMapper extends MyMapper<UserEntity> {
    default List<UserEntity> selectByName(String userName) {
        MyLambdaQueryWrapper<UserEntity> queryWrapper = new MyLambdaQueryWrapper<>();
        queryWrapper.eq(UserEntity::getName, userName);
        return  selectList(queryWrapper);
    }
}

:fire: 太棒了!现在就可以像操作 mybatisPlus 一样操作数据库了

接下来才是重点!!!!我会手把手教你如何实现这个功能。其实很简单,把这篇文章看完就能学会

首先我们要考虑几个问题!把这些问题解决完这个简易的 ORM 就能实现!

  • :question: 如何让 spring 扫描到,用户写的并继承了 MyMapper的接口?
  • :question: 如何为这些接口创建代理?
  • :question: 如何把代理被 spring 管理起来以便于用户使用@Resource 注入类中
  • :question: 如何实现类似 MybatisPlus 超级方便的LambdaQueryWrapper
  • :question: 如何优雅的借助 spring dataSouce 操作数据库
  • :question: 拿到数据库返回的结果后,如何映射到实体类上返回

如果你解决这几个问题,那实现一个简易的 ORM 还不是简简单单 :boom: :collision:

one:如何能扫描到用户继承了 MyMapper 的接口?

首先我们应该定义一个注解,当用户把注解放在启动类上的时候。扫描启动类下面的所有类看看那个是继承了MyMapper


        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.TYPE)
        @Documented
        @Import(MyBatisRegistrar.class)
        public @interface EnableMyBatis {
        }


        public class MyBatisRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

            @Override
            public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
                
                
            }
    
    
        }

核心在这里 @Import(MyBatisRegistrar.class) 由于你的注解上标注了这个。所以当用户使用你的 jar 包并在启动类上加上注解之后,spring 会找到这个类。并自动执行 registerBeanDefinitions 方法。这是 spring 提供的一种加载机制。那么好我们只需要在这个方法里面扫描我们想要的类就可以了


// 扫描启动类下面的类,找到实现了 MyMapper 的接口!
  String basePackage = ClassUtils.getPackageName(importingClassMetadata.getClassName());
  Set<Class<?>> classes = findMyMappers(basePackage);

two:如何为这些接口创建代理?

使用 JDK 的动态代理为接口生成代理对象。


@AllArgsConstructor
public class MyMapperProxy implements InvocationHandler {

    // 目标接口 userMapper.class
    private Class mapperInterface;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  
            // 代理其他方法
            DefaultMyMapperImpl defaultMyMapper = new DefaultMyMapperImpl(mapperInterface);
            defaultMyMapper.setInterfaceClass(mapperInterface);
            return method.invoke(defaultMyMapper, args);
        
    }


    @SuppressWarnings("unchecked")
    public static <T> T createProxy(Class<T> mapperInterface) {
        return (T) Proxy.newProxyInstance(
                mapperInterface.getClassLoader(),
                new Class[]{mapperInterface},
                new MyMapperProxy(mapperInterface)
        );
    }
}

通过该工具类,为扫描到的接口生成代理实例对象!

three:如何把代理被 spring 管理起来以便于用户使用@Resource 注入类中

将生成的代理对象注入我们需要构造一个 beanDefinition,将其编织到 spring 的生命周期当中


public class MyBatisRegistrar implements ImportBeanDefinitionRegistrar {

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

      
            // 将获取到的 interface 生成代理,注入 spring 当中
            for (Class<?> clazz : classes) {
                System.out.println("clazz = " + clazz);

                GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                beanDefinition.setBeanClass(clazz);
                beanDefinition.setInstanceSupplier(() -> MyMapperProxy.createProxy(clazz));

                String beanName = clazz.getSimpleName();
                beanName = Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
                System.out.println("注入beanName = " + beanName);
                registry.registerBeanDefinition(beanName, beanDefinition);
            }
       
    }
}

空文件

简介

核心功能扫描启动类下实现了 MyMapper 接口的接口。 展开 收起
Java
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Java
1
https://gitee.com/liu_liuliu/MyBatisDemo.git
git@gitee.com:liu_liuliu/MyBatisDemo.git
liu_liuliu
MyBatisDemo
MyBatisDemo
master

搜索帮助