Apache Calcite - 使用框架Reflective schema访问Java内存数据

devtools/2024/9/23 22:44:22/

前言

前文我们介绍了如何扩展实现自定义schema来访问Java内存数据,在Calcite框架中已提供了若干适配器,可用于访问不用来源的数据,简化我们的工作。 本节介绍 Reflective schema,通过这个适配器直接可以访问内存数据而不用额外扩展。

Reflective schema 简介

Apache Calcite中的ReflectiveSchema是一种机制,允许Calcite通过反射来访问Java对象作为数据库的模式。即可以将普通的Java对象集合作为数据库表来查询。ReflectiveSchema可以将Java对象的字段映射为表的列,对象的集合映射为表的行。

使用举例

在使用ReflectiveSchema时,需要定义一个包含Java对象集合的类。然后,将这个模式类添加到Calcite连接的根模式中,这样Calcite就能够通过SQL查询这些Java对象了。

例如,有一个Employee类和一个包含Employee对象列表的HrSchema类,可以使用ReflectiveSchema将HrSchema实例添加到Calcite的根模式中。可以执行SQL查询来访问HrSchema中的Employee对象了。

使用ReflectiveSchema的步骤大致如下:

  • 定义Java对象类(例如Employee)。
  • 定义包含Java对象集合的模式类(例如HrSchema)。
  • 创建一个Calcite连接,并将模式类的实例添加到连接的根模式中。
  • 通过SQL查询Java对象。
  • 这种方式使得在不需要将数据存储在传统数据库中的情况下,直接在内存中的Java对象上执行SQL查询成为可能。
java">public class HrSchema {public Employee[] employees;public HrSchema(Employee[] employees) {this.employees = employees;}public Employee[] getEmployees() {return employees;}}
java">    @Testpublic void test() throws Exception{Employee[] employees = new Employee[]{new Employee( "Alice","1", "50000"),new Employee( "Bob","2", "60000"),new Employee( "Charlie","3", "70000")};Properties info = new Properties();info.setProperty("lex","JAVA");Connection connection = DriverManager.getConnection("jdbc:calcite:", info);CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class);SchemaPlus rootSchema = calciteConnection.getRootSchema();rootSchema.add("hr", new ReflectiveSchema(new HrSchema(employees)))ResultSet resultSet = calciteConnection.createStatement().executeQuery("SELECT * FROM hr.employees Where id > 2");while (resultSet.next()) {System.out.println(resultSet.getString("id") + ", " +resultSet.getString("name") + ", " +resultSet.getString("deptId"));}resultSet.close();connection.close();}

上述代码我们先定义了类 HrSchema,其内部包含对象数组employees

接着创建ReflectiveSchema对象,构造函数接收一个Object对象参数,target限制如下

  • 是一个对象
  • 内部包含的成员必须是数组对象,可以有一个或多个成员,每个成员将映射为一张表
java">  public ReflectiveSchema(Object target) {super();this.clazz = target.getClass();this.target = target;}

将该schema加入到root schema中,我们接下来就可以直接使用sql查询数组中的数据了

ReflectiveSchema 源码解读

ReflectiveSchema 帮助我们实现了功能,其核心做的事情还是创建table对象、table字段类型描述类型对象。创建table的逻辑如下。通过反射获取类成员变量,接着通过fieldRelation将每个合适的成员创建为table。

java">  private Map<String, Table> createTableMap() {final ImmutableMap.Builder<String, Table> builder = ImmutableMap.builder();for (Field field : clazz.getFields()) {final String fieldName = field.getName();final Table table = fieldRelation(field);if (table == null) {continue;}builder.put(fieldName, table);}Map<String, Table> tableMap = builder.build();// Unique-Key - Foreign-Keyfor (Field field : clazz.getFields()) {if (RelReferentialConstraint.class.isAssignableFrom(field.getType())) {RelReferentialConstraint rc;try {rc = (RelReferentialConstraint) field.get(target);} catch (IllegalAccessException e) {throw new RuntimeException("Error while accessing field " + field, e);}requireNonNull(rc, () -> "field must not be null: " + field);FieldTable table =(FieldTable) tableMap.get(Util.last(rc.getSourceQualifiedName()));assert table != null;List<RelReferentialConstraint> referentialConstraints =table.getStatistic().getReferentialConstraints();if (referentialConstraints == null) {// This enables to keep the same Statistics.of belowreferentialConstraints = ImmutableList.of();}table.statistic =Statistics.of(ImmutableList.copyOf(Iterables.concat(referentialConstraints,Collections.singleton(rc))));}}return tableMap;}

创建table的核心逻辑中getElementType获取元素类型,只有是数组时才能拿到数组元素类型。

java">  private <T> @Nullable Table fieldRelation(final Field field) {final Type elementType = getElementType(field.getType());if (elementType == null) {return null;}Object o;try {o = field.get(target);} catch (IllegalAccessException e) {throw new RuntimeException("Error while accessing field " + field, e);}requireNonNull(o, () -> "field " + field + " is null for " + target);@SuppressWarnings("unchecked")final Enumerable<T> enumerable = toEnumerable(o);return new FieldTable<>(field, elementType, enumerable);}private static @Nullable Type getElementType(Class clazz) {if (clazz.isArray()) {return clazz.getComponentType();}if (Iterable.class.isAssignableFrom(clazz)) {return Object.class;}return null; // not a collection/array/iterable}

结果上面两个核心流程,基于反射,Table、RelDataType关键对象就创建完毕。核心过程总结如下

  1. 从类中获取成员变量, 每一个成员变量对应一个Table
  2. 检查是否是数组类型
  3. 如果是数组类型获取数组元素类型
  4. 通过读取数组元素类型构造RelDataType
  5. 完成Table的构造

总结

基于ReflectiveSchema适配器可以简化我们的工作,提升效率。但该工具有一定局限性 ,要么调整使用方式,要么改写核心方法


http://www.ppmy.cn/devtools/53658.html

相关文章

常量池你了解多少

第1部分&#xff1a;引言 JVM简介 Java虚拟机&#xff08;JVM&#xff09;是一个可以执行Java字节码的虚拟计算机。它是Java平台的核心组成部分&#xff0c;允许Java程序在不同的操作系统和硬件平台上运行。JVM不仅提供了内存管理、垃圾回收等基础服务&#xff0c;还支持多种…

服务器被墙是什么原因,怎么解决服务器被墙

服务器被墙通常是由于以下几个原因&#xff1a; 网络监管&#xff1a;某些国家或地区会对网络进行严格的监管&#xff0c;包括对特定网站、应用程序或服务进行屏蔽或封锁。这种情况下&#xff0c;服务器可能会被封锁&#xff0c;导致无法访问。 安全问题&#xff1a;服务器被发…

MySQL支持哪些特殊字符

MySQL支持多种特殊字符&#xff0c;这些字符在SQL语句中具有特定的含义&#xff0c;需要在使用时特别注意。以下是一些MySQL中的特殊字符及其相关信息&#xff1a; 引号&#xff1a; 单引号&#xff08;&#xff09;&#xff1a;用于定义字符串。如果字符串中包含单引号本身&…

使用Stream实现Web应用,使用YOLOv8模型对图像进行目标检测为例。

Streamlit是一个开源的Python框架&#xff0c;专门设计用于快速构建和共享数据应用程序。它使数据科学家和机器学习工程师能够通过编写简单的Python脚本&#xff0c;轻松创建美观、功能强大的Web应用程序&#xff0c;而无需具备前端开发的经验。 其他框架或web应用可以看下面两…

四连杆机构运动学仿真 | Matlab源码+理论文本【超详细】

【程序简介】&#x1f4bb;&#x1f50d; 本程序通过matlab实现了四连杆机构的运动学仿真编程&#xff0c;动态展现了四连杆机构的角位移、角速度和角加速度的时程曲线&#xff0c;除了程序本身&#xff0c;还提供了机构运动学详细的公式推导文档&#xff0c;从而帮助大家更好…

深入理解指针(四)

目录 1. 回调函数是什么? ​2. qsort使用举例 2.1冒泡排序 2.2使用qsort函数排序整型数据 ​2.3 使用qsort排序结构数据(名字) 2.4 使用qsort排序结构数据(年龄) 3. qsort函数的模拟实现 1. 回调函数是什么? 回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数…

EasyCVR/EasyDSS无人机直播技术助力野生动物监测

近日有新闻报道&#xff0c;一名挖掘机师傅在清理河道时&#xff0c;意外挖出一只稀有的扬子鳄&#xff0c;挖机师傅小心翼翼地将其放在一边&#xff0c;扬子鳄也顺势游回一旁的河道中。 随着人类对自然环境的不断探索和开发&#xff0c;野生动物及其栖息地的保护显得愈发重要。…

[Linux] TCP协议介绍(3): TCP协议的“四次挥手“过程、状态分析...

TCP协议是面向连接的 上一篇文章简单分析了TCP通信非常重要的建立连接的"三次握手"的过程 本篇文章来分析TCP通信中同样非常重要的断开连接的"四次挥手"的过程 TCP的"四次挥手" TCP协议建立连接 需要"三次握手". "三次挥手&q…