metamodel
最近, Human Inference和Apache软件基金会 (ASF)宣布捐赠并接受对MetaModel项目的孵化。 以前,MetaModel可以通过LGPL许可获得,并由Human Inference的产品开发进行管理,但是现在已移至ASF,获得了新的许可,社区和治理。 那么,这个项目的全部目的是什么?它有什么用?
MetaModel是一个Java库,旨在提供一个用于与任何数据存储进行交互的接口; 可以是关系数据库,NoSQL数据库,电子表格文件或其他文件格式。 通过交互,我们意味着探索元数据,查询和写入/更改存储中包含的数据。 当然,任何这样的抽象都会遗漏细节,因此存在过度概括和失去重要功能的风险。 您不希望将关系SQL数据库的功能简化为仅使用全表扫描( SELECT * FROM [table] ),例如查询。 另一方面,除了特定SQL Server品牌和版本之外,您也不想公开在其他任何数据存储上都无法使用的功能。 最后,您将希望利用现有的通用技能与数据进行交互,例如SQL。
那么,由MetaModel项目选择的数据存储抽象方法是什么? 该项目通过与SQL非常相似的Java接口(或可选地从String解析)公开了查询模型。 由于查询被定义为常规Java对象,因此可以轻松解释它,并且-根据底层技术-将选择实际执行查询的最佳策略。 这意味着MetaModel不仅提供接口,而且还提供接口。 它还包括一个完整的查询引擎,可以适合于处理查询中涉及的某些或所有任务。 对于关系JDBC数据库,查询执行的99%仍将在数据库的本机引擎中进行。 但是,使用MetaModel,您还可以在CSV文件或Excel电子表格上触发相同的查询,从而利用MetaModel的查询引擎正确地对数据进行切片和切块。 您根本不需要更改查询。
当然,这是假设元数据和数据存储的结构兼容的。 不同的数据存储区具有公开或推断其元数据的不同方式。 JDBC数据库通常通过JDBC元数据API公开其元数据。 CSV和Excel工作表等文件格式的定义不太明确; 他们通过读取文件的标题行来浏览元数据。 并且,作为一个极端的示例,有多个NoSQL数据库明确没有元数据。 MetaModel为您提供了以编程方式指定元数据或通过检查数据存储的前N条记录来推断元数据的选项。
MetaModel的最核心构造是DataContext接口,它表示数据存储并用于浏览和查询它。 另外,UpdateableDataContext子接口可用于可在其中执行数据更新的可写数据存储。 只要确保您具有DataContext实例,就可以使用基本的代码完成来或多或少地学习整个库。 以下是几个常见DataContext实现的示例以及如何实例化它们:
// a DataContext for a CSV file
UpdateableDataContext csv = new CsvDataContext(new File(“data.csv”));
// a DataContext for an Excel spreadsheet
UpdateableDataContext excel = new ExcelDataContext(new File(“spreadsheet.xlsx”));
// a DataContext for a JDBC database (can use either DataSource or Connection)
java.sql.DataSource dataSource = …
UpdateableDataContext jdbc = new JdbcDataContext(dataSource);
// a DataContext for an XML file (where metadata is automatically inferred)
DataContext xml = new XmlDomDataContext(new File(“data.xml”));
// a DataContext for connecting to Salesforce.com’s data web services
UpdateableDataContext salesforce =
new SalesforceDataContext(username, pw, securityToken);
// a in-memory DataContext for POJOs (useful for testing and mocking)
Person record1 = ...
Person record2 = ...
TableDataProvider tableDataProvider = new ObjectTableDataProvider(
“persons”, Person.class, Arrays.asList(record1, record2));
UpdateableDataContext pojos = new PojoDataContext(“schema”, tableDataProvider);
元数据对于MetaModel不仅对于探索数据结构,而且对于定义查询都很重要。 如果您只是在使用适当的元数据,则要付出很多努力来确保可以安全地执行查询。 因此,在查询之前,作为开发人员,您需要做的第一件事就是掌握元数据对象。 例如,如果您知道有一个名为ORDER_LINE的表,其中包含一个price列和一个order_id列,则查询该表所需的元数据可以以通常的硬编码方式进行解析(显然,只有在您知道数据存储时,该表才起作用):
DataContext dataContext = ... // the DataContext object represents the ‘connection’
Table orderLines = dataContext.getTableByQualifiedLabel(“ORDER_LINES”);
Column price = orderLines.getColumnByName(“price”);
Column orderId = orderLines.getColumnByName(“order_id”);
但是,API还允许您基于发现动态获取元数据。 这对于希望向用户显示可用表,列等并让用户自己做出影响查询的选择的应用程序非常有用:
Schema[] schema = dataContext.getSchemas();
Table[] tables = schemas[0].getTables();
Column[] columns = tables[0].getColumns();
MetaModel的另一个重要方面是将元数据,查询和数据交互周围的其他实体视为对象。 MetaModel中的查询是一个常规Java对象,您可以在执行前对其进行操作和传递。 这使应用程序可以创建复杂的工作流,其中不同的代码段可以参与查询计划的构建和优化,而不必转向繁琐SQL字符串操作。 它也有助于类型安全,因为例如Query模型基于类型安全的构造(如列,表等),而不是模糊的String文字。
因此,让我们看看MetaModel中的查询是什么样的。
您可以通过三种方式触发相同的查询:
1.从头开始编写:
这是传统的面向POJO的方式。 它很冗长,但是可以提供您想要的所有灵活性。
Query q = new Query();
q.select(SUM, price);
q.select(orderId);
q.from(orderLines);
q.groupBy(orderId);
q.setMaxRows(100);
DataSet dataSet = dataContext.executeQuery(q);
2. 使用流畅的Builder API:
添加了Builder API,以允许另一种类型安全的查询方式,但具有较少的冗长性。 同样,此API通过builder-pattern为开发人员提供了有关下一步在逻辑上填充查询的子句的指示。 当只有一个用于构建查询的组件时,这是定义查询的首选方式:
Query q = dataContext.query().from(orderLines)
.select(SUM, price).and(orderId)
.groupBy(orderId).maxRows(100).toQuery();
DataSet dataSet = dataContext.executeQuery(q);
3. 从字符串中解析它:
有时您可能想偷工减料,而退回到更传统SQL字符串方法。 MetaModel也可以解析来自纯字符串的查询,但是它具有类型安全性较低的风险,因为只能在运行时验证字符串查询。
Query q = dataContext.parseQuery(
“SELECT SUM(price), order_id FROM order_lines GROUP BY order_id LIMIT 100”);
DataSet dataSet = dataContext.executeQuery(q);
如您所见,这三种方法的最终结果都是一个DataSet,这是一种表示表格查询结果的对象类型。 无需过多介绍所有DataSet功能,您可以像这样简单地对其进行遍历:
Try {
while (dataSet.next()) {
Row row = dataSet.getRow();
System.out.println(row.toString());
}
} finally {
dataset.close();
}
使用MetaModel执行更新是通过类似的类型安全和元数据驱动的方法执行的。 如上所述,并非所有数据存储都是可写的,这就是为什么您需要一个同时实现UpdateableDataContext接口的DataContext对象的原因。 鉴于此,让我们尝试在示例中更新订单数据:
dataContext.executeUpdate(new UpdateScript() {
@Override
public void run(UpdateCallback cb) {
// insert a new order line
cb.insertInto(orderLines).value(orderId, 123).value(price, 395).execute();
// update the price of orderlines where order_id = 122
cb.update(orderLines).where(orderId).eq(122).value(price, 295).execute();
}
});
请注意,UpdateScript是用于设置逻辑事务边界的构造。 根据基础数据技术,将应用适当的交易策略。 JDBC数据库将应用ACID事务,大多数文件格式将使用同步写入等。 最终结果是,您可以使用一种语法在所有数据存储中写入数据。
由于匿名内部类的存在,此处的语法不是特别漂亮。 显然,使用Java 8中的Closures可以改善这种情况。但是,如果您只想执行单个操作,则可以直接使用几个方便的预构建UpdateScript类:
dataContext.executeUpdate(
new InsertInto(orderLines).value(orderId, 123).value(price, 395));
此外,executeUpdate方法可用于创建和删除表以及删除记录。
最后,MetaModel的专家用户可能会问' 如果我要连接到[XYZ],该怎么办? '(其中XYZ是我们尚不支持的奇异数据存储)。 显然,我们希望MetaModel易于扩展; 这是使查询引擎可插入的部分原因。 您需要做的是构建自己的DataContext接口实现; 但是,如果您从头开始,那并不是一件容易的事。 因此,我们提供了具有许多扩展点的抽象实现。 这是演练:
在本文中,我们介绍了MetaModel,它是一个提供对各种数据存储的访问的库,解释了如何处理元数据,如何查询存储以及如何执行更新。
将来,我们打算在Apache的新家中进一步开发MetaModel。 我们将为HBase,Cassandra和其他现代数据库添加更多内置的DataContext实现。 以及进一步扩展元数据可以使用的功能集。 我们正在研究的一些想法是关于嵌套结构(地图和列表,在许多NoSQL数据库中都可用)的更丰富的元数据,创建虚拟表的能力(类似于VIEW,但在客户端而不是服务器上得到了便利),支持将POJO映射到DataSet行,并将更多功能插入查询引擎。
Apache MetaModel目前正在The Apache Software Foundation进行孵化。 如果您对此项目感兴趣,请在Apache Incubator MetaModel页面上找到邮件列表,错误跟踪等。
metamodel