【开源项目】Stream-Query的入门使用和原理分析

news2024/12/29 9:34:44

前言

无意间发现了一个有趣的项目,Stream-Query。了解了一下其基本的功能,可以帮助开发者省去Mapper的编写。在开发中,我们会编写entity和mapper来完成业务代码,但是Stream-Query可以省去mapper,只写entity。

快速入门

实体类

@Data
public class UserInfo{

    private static final long serialVersionUID = -7219188882388819210L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private String name;
    private Integer age;
    private String email;
}

创表语句

create table user_info
(
    id   bigint auto_increment comment '主键'
        primary key,
    name varchar(20) null comment '姓名',
    age  int comment '年龄',
    email  varchar(20)  comment '邮件' 
)comment '用户信息';

配置扫描包

@EnableMybatisPlusPlugin(basePackages = "com.charles.entity.**")

插入Demo

    @GetMapping("/t1")
    public void t1() {
        UserInfo userInfo = new UserInfo();
        userInfo.setAge(12);
        userInfo.setEmail("123@qq.com");
        userInfo.setName("张三");
        UserInfo userInfo2 = new UserInfo();
        userInfo2.setAge(123);
        userInfo2.setEmail("123@qq.com");
        userInfo2.setName("李四");
        Database.saveFewSql(Arrays.asList(userInfo, userInfo2));
    }

单个查询Demo

    @GetMapping("t2")
    public void t2() {
        UserInfo userInfo = One.of(UserInfo::getId).eq(2L).query();
        System.out.println(userInfo);
    }

多个查询Demo

    @GetMapping("t3")
    public void t3() {
        QueryCondition.query(UserInfo.class)
                .in(UserInfo::getName, Lists.of("张三", "李四"));

        QueryCondition<UserInfo> wrapper =
                QueryCondition.query(UserInfo.class)
                        .in(UserInfo::getName, Lists.of("张三", "李四"));

        List<UserInfo> list = Database.list(wrapper);
        Map<Long, UserInfo> idUserMap = OneToOne.of(UserInfo::getId).eq(1L).query();
        System.out.println(list);
    }

Stream-Query通过Database,One,Many等静态方法完成查询和插入等操作。

核心原理分析

EnableMybatisPlusPlugin注入了StreamConfigurationSelector

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Inherited
@Import({StreamConfigurationSelector.class})
public @interface EnableMybatisPlusPlugin {

  /**
   * Alias for {@link #basePackages()}
   *
   * @return base packages
   */
  String[] value() default {};

  /**
   * Base packages
   *
   * @return base packages
   */
  String[] basePackages() default {};
}

StreamConfigurationSelector注入了StreamScannerRegistrar扫描注册器和StreamPluginAutoConfiguration配置类。

public class StreamConfigurationSelector implements DeferredImportSelector, Ordered {

  @Override
  public String[] selectImports(AnnotationMetadata metadata) {
    return new String[] {
      StreamScannerRegistrar.class.getName(), StreamPluginAutoConfiguration.class.getName()
    };
  }

  @Override
  public int getOrder() {
    return HIGHEST_PRECEDENCE;
  }
}

StreamScannerRegistrar注入了StreamScannerConfigurer扫描类。

public class StreamScannerRegistrar implements ImportBeanDefinitionRegistrar {

  @Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes annotationAttributes =
        AnnotationAttributes.fromMap(
            importingClassMetadata.getAnnotationAttributes(
                EnableMybatisPlusPlugin.class.getName()));
    if (Objects.isNull(annotationAttributes)) {
      return;
    }
    BeanDefinitionBuilder builder =
        BeanDefinitionBuilder.genericBeanDefinition(StreamScannerConfigurer.class);
    Set<String> basePackages = new HashSet<>();
    basePackages.addAll(
        Arrays.stream(annotationAttributes.getStringArray("value"))
            .filter(StringUtils::hasText)
            .collect(Collectors.toSet()));
    basePackages.addAll(
        Arrays.stream(annotationAttributes.getStringArray("basePackages"))
            .filter(StringUtils::hasText)
            .collect(Collectors.toSet()));
    basePackages.addAll(
        Arrays.stream(annotationAttributes.getClassArray("basePackageClasses"))
            .filter(Objects::nonNull)
            .map(ClassUtils::getPackageName)
            .collect(Collectors.toSet()));
    if (basePackages.isEmpty()) {
      basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
      builder.addPropertyValue("emptyBasePackages", true);
    }
    builder.addPropertyValue("basePackages", basePackages);

    Set<Class<?>> classes =
        Arrays.stream(annotationAttributes.getClassArray("classes"))
            .filter(Objects::nonNull)
            .collect(Collectors.toSet());
    builder.addPropertyValue("classes", classes);

    Class<? extends Annotation> annotation = annotationAttributes.getClass("annotation");
    if (!Annotation.class.equals(annotation)) {
      builder.addPropertyValue("annotation", annotation);
    }

    Class<?> scanInterface = annotationAttributes.getClass("interfaceClass");
    if (!Class.class.equals(scanInterface)) {
      builder.addPropertyValue("interfaceClass", scanInterface);
    }

    registry.registerBeanDefinition("streamScannerConfigurer", builder.getBeanDefinition());
  }
}

StreamScannerConfigurer实现了BeanFactoryPostProcessorStreamScannerConfigurer#postProcessBeanFactory可以根据注解扫描,可以根据接口扫描,可以根据扫描包扫描。详情可见 enablemybatisplusplugin。

  @Override
  public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
      throws BeansException {
    defaultScanConfig();
    // 指定类
    registerEntityClasses(this.classes);
    StreamClassPathScanner scanner = new StreamClassPathScanner(false);
    scanner.setAnnotation(this.annotation);
    scanner.setInterfaceClass(this.interfaceClass);
    scanner.registerFilters();
    Set<Class<?>> classSet = scanner.scan(this.basePackages);
    registerEntityClasses(classSet);
  }

StreamPluginAutoConfiguration配置类中注入了DynamicMapperHandler

  @Bean
  @ConditionalOnMissingBean(DynamicMapperHandler.class)
  public DynamicMapperHandler dynamicMapperHandler(
      SqlSessionFactory sqlSessionFactory, StreamScannerConfigurer streamScannerConfigurer) {
    return new DynamicMapperHandler(sqlSessionFactory, streamScannerConfigurer.getEntityClasses());
  }

DynamicMapperHandler该类的作用就是根据传入的entity的列表,构建Mapper。

public class DynamicMapperHandler {

  public DynamicMapperHandler(
      SqlSessionFactory sqlSessionFactory, Collection<Class<?>> entityClassList) {
    Configuration configuration = sqlSessionFactory.getConfiguration();
    if (configuration instanceof MybatisConfiguration) {
      MybatisConfiguration mybatisConfiguration = (MybatisConfiguration) configuration;
      entityClassList.forEach(
          entityClass -> Database.buildMapper(mybatisConfiguration, entityClass));
    }
  }
}

Database#buildMapper,根据ByteBuddy来生成对应的接口实现。

  public static void buildMapper(Configuration configuration, Class<?> entityClass) {
    if (!(configuration instanceof MybatisConfiguration)) {
      throw new IllegalArgumentException("configuration must be MybatisConfiguration");
    }
    Maps.computeIfAbsent(
        ENTITY_MAPPER_CLASS_CACHE,
        entityClass,
        k -> {
          Class<?> dynamicMapper =
              new ByteBuddy()
                  .makeInterface(
                      TypeDescription.Generic.Builder.parameterizedType(IMapper.class, entityClass)
                          .build())
                  .name(
                      String.format(
                          "%s.%sMapper",
                          PluginConst.DYNAMIC_MAPPER_PREFIX, entityClass.getSimpleName()))
                  .make()
                  .load(ClassUtils.class.getClassLoader())
                  .getLoaded();
          configuration.addMapper(dynamicMapper);
          return dynamicMapper;
        });
  }

以上就是项目初始化的流程,StreamQuery帮助我们完成了根据Entity来自动生成Mapper,接下来我们分析一下StreamQuery是如何帮助我们简化使用的。

Database#saveFewSql(java.util.Collection<T>),保存操作,获取SqlSession,获取IMapper,执行saveFewSql的方法。

  public static <T> boolean saveFewSql(Collection<T> entityList) {
    return saveFewSql(entityList, PluginConst.DEFAULT_BATCH_SIZE);
  }

  public static <T> boolean saveFewSql(Collection<T> entityList, int batchSize) {
    if (CollectionUtils.isEmpty(entityList) || batchSize <= 0) {
      return false;
    }
    return execute(
        getEntityClass(entityList),
        (IMapper<T> baseMapper) ->
            entityList.size() == baseMapper.saveFewSql(entityList, batchSize));
  }

  public static <T, R, M extends BaseMapper<T>> R execute(
      Class<T> entityClass, SFunction<M, R> sFunction) {
    SqlSession sqlSession = SqlHelper.sqlSession(entityClass);
    try {
      return sFunction.apply(getMapper(entityClass, sqlSession));
    } finally {
      SqlSessionUtils.closeSqlSession(
          sqlSession, GlobalConfigUtils.currentSessionFactory(entityClass));
    }
  }

IMapper#saveFewSql,默认实现是批量拆分List,调用saveOneSql

  /**
   * 批量插入
   *
   * @param list 集合
   * @param batchSize 分割量
   * @return 是否成功
   */
  default long saveFewSql(Collection<T> list, int batchSize) {
    return Steam.of(list).splitList(batchSize).mapToLong(this::saveOneSql).sum();
  }

补充了解

One

One,返回单个实体类。通过封装Database来完成查询单个操作。

  /**
   * query.
   *
   * @return a V object
   */
  public V query() {
    return Sf.of(Database.getOne(wrapper)).mayAlso(peekConsumer).mayLet(valueOrIdentity()).get();
  }

QueryCondition

QueryCondition查询条件类,继承了LambdaQueryWrapper

也就是new LambdaQueryWrapper<UserInfo>().in(UserInfo::getName, Lists.of("张三", "李四"));等于QueryCondition<UserInfo> wrapper = QueryCondition.query(UserInfo.class).in(UserInfo::getName, Lists.of("张三", "李四"));

public class QueryCondition<T> extends LambdaQueryWrapper<T> {

  public static <T> QueryCondition<T> query(Class<T> entityClass) {
    QueryCondition<T> condition = new QueryCondition<>();
    condition.setEntityClass(entityClass);
    return condition;
  }

OneToOne

OneToOne封装了一层Stream的操作。

  public Map<K, V> query() {
    return query(HashMap::new);
  }

  /**
   * query.
   *
   * @param mapFactory a {@link java.util.function.IntFunction} object
   * @param <R> a R class
   * @return a R object
   */
  public <R extends Map<K, V>> R query(IntFunction<R> mapFactory) {
    List<T> list = Database.list(wrapper);
    return Steam.of(list)
        .parallel(isParallel)
        .peek(peekConsumer)
        .toMap(
            keyFunction,
            valueOrIdentity(),
            SerBiOp.justAfter(),
            () -> mapFactory.apply(list.size()));
  }

AsyncHelper

AsyncHelper使用

    public static void main(String[] args) {
        List<String> result = AsyncHelper.supply(() -> {
            System.out.println(Thread.currentThread().getName() + "1111");
            return "123";
        }, () -> {
            System.out.println(Thread.currentThread().getName() + "2345");
            return "456";
        });
        System.out.println(result);

    }

原理分析,可以指定拦截器和线程池,使用CompletableFuture.supplyAsync来完成异步执行。

    @SafeVarargs
    public static <T> List<T> supply(AsyncConfig asyncConfig, SerSupp<T>... suppliers) {
        AsyncInterceptor interceptor = asyncConfig.getInterceptor();
        interceptor.before();
        CompletableFuture<T>[] futures = (CompletableFuture[])Steam.of(suppliers).map((supplier) -> {
            return CompletableFuture.supplyAsync(() -> {
                return interceptor.execute(supplier);
            }, asyncConfig.getExecutor());
        }).toArray((x$0) -> {
            return new CompletableFuture[x$0];
        });
        CompletableFuture var10000 = CompletableFuture.allOf(futures);
        interceptor.getClass();
        CompletableFuture<Void> exceptionally = var10000.exceptionally(interceptor::onError);
        (() -> {
            return asyncConfig.getTimeout() == -1 ? exceptionally.get() : exceptionally.get((long)asyncConfig.getTimeout(), asyncConfig.getTimeUnit());
        }).get();
        interceptor.after();
        return Steam.of(futures).map(CompletableFuture::get).toList();
    }

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/886928.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JVM中对象和GC Root之间的四种引用关系

1. 强引用 只有所有 GC Roots 对象都不通过【强引用】引用该对象&#xff0c;该对象才能被垃圾回收 由GC Root直接new出来的对象是强引用&#xff0c;只有当GC Root不再引用该对象的时候&#xff0c;才会被回收 例子&#xff1a; List<String> list new ArrayList<&…

安防监控视频云存储平台EasyNVR通道频繁离线的原因排查与解决

安防视频监控汇聚EasyNVR视频集中存储平台&#xff0c;是基于RTSP/Onvif协议的安防视频平台&#xff0c;可支持将接入的视频流进行全平台、全终端分发&#xff0c;分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。为了满足用户的集成与二次开发需求&#xf…

uni-app引入sortable列表拖拽,兼容App和H5,拖拽排序。

效果: 拖拽排序 背景&#xff1a; 作为一名前端开发人员&#xff0c;在工作中难免会遇到拖拽功能&#xff0c;分享一个github上一个不错的拖拽js库&#xff0c;能满足我们在项目开发中的需要&#xff0c;下面是我在uniapp中使用SortableJS的使用详细流程&#xff1b; vue开发…

小程序体验版不存在 无法体验

1、权限问题&#xff1a; 1、开发者有所有权限。 2、小程序访问路径也是正确的。 该有的权限都有了。 2、解决办法&#xff1a; 打开微信公众平台&#xff0c;左侧菜单【设置】- 【第三方设置】&#xff0c;取消授权即可。

设计模式十七:迭代器模式(Iterator Pattern)

迭代器模式&#xff08;Iterator Pattern&#xff09;是一种行为型设计模式&#xff0c;它提供了一种访问聚合对象&#xff08;例如列表、集合、数组等&#xff09;中各个元素的方法&#xff0c;而无需暴露其内部表示。迭代器模式将遍历元素和访问元素的责任分离开来&#xff0…

jenkins一键部署github项目

个人目前理解jenkins部署分为两步&#xff1a; 构建项目&#xff0c;如生成jar自动执行sh脚本 如果没有jenkins&#xff0c;我们可能需要将jar移动到服务器&#xff0c;然后执行java -jar跑程序&#xff0c;jenkins可以替代我们执行这些东西&#xff0c;下面从0开始&#xff0…

近 2000 台 Citrix NetScaler 服务器遭到破坏

Bleeping Computer 网站披露在某次大规模网络攻击活动中&#xff0c;一名攻击者利用被追踪为 CVE-2023-3519 的高危远程代码执行漏洞&#xff0c;入侵了近 2000 台 Citrix NetScaler 服务器。 研究人员表示在管理员安装漏洞补丁之前已经有 1200 多台服务器被设置了后门&#x…

Command Injection

Command Injection Command Injection&#xff0c;即命令注入&#xff0c;是指通过提交恶意构造的参数破坏命令语句结构&#xff0c;从而达到执行恶意命令的目的。PHP命令注入攻击漏洞是PHP应用程序中常见的脚本漏洞之一。 PHP命令注入漏洞的函数 systme()、exec()、shell_ex…

强化学习:用Python训练一个简单的机器人

一、介绍 强化学习&#xff08;RL&#xff09;是一个令人兴奋的研究领域&#xff0c;它使机器能够通过与环境的交互来学习。在这篇博客中&#xff0c;我们将深入到RL的世界&#xff0c;并探索如何使用Python训练一个简单的机器人。在本文结束时&#xff0c;您将对 RL 概念有基本…

基于令牌级 BERT 嵌入的趋势生成句子级嵌入

一、说明 句子&#xff08;短语或段落&#xff09;级别嵌入通常用作许多 NLP 分类问题的输入&#xff0c;例如&#xff0c;在垃圾邮件检测和问答 &#xff08;QA&#xff09; 系统中。在我上一篇文章发现不同级别的BERT嵌入的趋势中&#xff0c;我讨论了如何生成一个向量表示&a…

H3C交换机如何配置本地端口镜像并在PC上使用Wireshake抓包

环境: H3C S6520-26Q-SI version 7.1.070, Release 6326 Win 10 专业版 Wireshake Version 4.0.3 问题描述: H3C交换机如何配置本地端口镜像并在PC上使用Wireshake抓包 解决方案: 配置交换机本地端口镜像 1.进入系统视图,并创建本地镜像组1 <H3C>system-vie…

Unity如何把游戏导出成手机安装包

文章目录 前言使用环境步骤添加场景构建APK 前言 本文章主要演示了&#xff0c;如何将制作好的游戏&#xff0c;导出成APK&#xff0c;安装到手机上。 使用环境 Unity2022。 步骤 首先打开你的项目&#xff0c;然后选择菜单栏的“File” > “Build Settings…”&#xf…

C++中List的实现

前言 数据结构中&#xff0c;我们了解到了链表&#xff0c;但是我们使用时需要自己去实现链表才能用&#xff0c;但是C出现了list将这一切皆变为现。list可以看作是一个带头双向循环的链表结构&#xff0c;并且可以在任意的正确范围内进行增删查改数据的容器。list容器一样也是…

每天一道leetcode:1926. 迷宫中离入口最近的出口(图论中等广度优先遍历)

今日份题目&#xff1a; 给你一个 m x n 的迷宫矩阵 maze &#xff08;下标从 0 开始&#xff09;&#xff0c;矩阵中有空格子&#xff08;用 . 表示&#xff09;和墙&#xff08;用 表示&#xff09;。同时给你迷宫的入口 entrance &#xff0c;用 entrance [entrancerow, …

7.原 型

7.1原型 【例如】 另外- this指向&#xff1a; 构造函数和原型对象中的this都指向实例化的对象 7.2 constructor属性 每个原型对象里面都有个constructor属性( constructor构造函数) 作用&#xff1a;该属性指向该原型对象的构造函数 使用场景: 如果有多个对象的方法&#…

如何使用Kali Linux进行密码破解?

今天我们探讨Kali Linux的应用&#xff0c;重点是如何使用它来进行密码破解。密码破解是渗透测试中常见的任务&#xff0c;Kali Linux为我们提供了强大的工具来帮助完成这项任务。 1. 密码破解简介 密码破解是一种渗透测试活动&#xff0c;旨在通过不同的方法和工具来破解密码…

ubuntu 编译安装nginx及安装nginx_upstream_check_module模块

一、下载安装包 # 下载nginx_upstream_check_module模块 wget https://codeload.github.com/yaoweibin/nginx_upstream_check_module/zip/master# 解压 unzip master# 下载nginx 1.21.6 wget https://github.com/nginx/nginx/archive/refs/tags/release-1.21.6.tar.gz # 解压…

【Spring 】了解Spring AOP

目录 一、什么是Spring AOP 二、AOP的使用场景 三、AOP组成 四、Spring AOP的实现 1、添加Spring AOP依赖 2、定义切面和切点 3、定义相关通知 五、 AOP的实现原理 1、什么是动态代理 2、 JDK代理和CGLIB代理的区别 一、什么是Spring AOP AOP&#xff08;Aspect Ori…

机器学习|Softmax 回归的数学理解及代码解析

机器学习&#xff5c;Softmax 回归的数学理解及代码解析 Softmax 回归是一种常用的多类别分类算法&#xff0c;适用于将输入向量映射到多个类别的概率分布。在本文中&#xff0c;我们将深入探讨 Softmax 回归的数学原理&#xff0c;并提供 Python 示例代码帮助读者更好地理解和…

Java之SpringCloud Alibaba【四】【微服务 Sentinel服务熔断】

Java之SpringCloud Alibaba【四】【微服务 Sentinel服务熔断】 一、分布式系统遇到的问题1、服务挂掉的一些原因 二、解决方案三、Sentinel&#xff1a;分布式系统的流量防卫兵1、Sentinel是什么2、Sentinel和Hystrix对比3、Sentinel快速开发4、通过注解的方式来控流5、启动Sen…