DevX 开源组件 DevXRoutingDataSource SQL 解析测试篇
DevXRoutingDataSource 的核心功能也是最基础的理念是根据 SQL 语句去进行多数据源的路由, 所以 SQL 解析是其中很重要的一个环节。为了保障开源项目的质量,我们会编写很多的测试代码,从各个角度对代码进行测试。本章内容是关于对 SQL 解析进行测试。
SQL 解析实现
目前提供了两种实现,一种基于 Apache Calcite
(github.com/apache/calc…) (开发中) , 一种基于 JSqlParser
(github.com/JSQLParser/…)
JSqlParser
基于 Visitor
设计模式编写. Visitor
设计模式是一种行为型设计模式,它允许你将算法与其所作用的对象分离开来。通过这种方式,你可以在不修改对象结构的情况下向现有对象结构添加新的操作。它的主要思想是将操作(也称为 Visitor)与元素(也称为 Visitable)分离开来。Visitor 通过访问元素并执行操作来实现其目的。这种模式通常用于对象结构包含多种类型的对象,而你希望对这些对象进行一些共同的操作,同时又不希望将这些操作添加到对象本身中。
下面是关于如何使用 JSqlParser
的简单示例.
Parse the SQL Text into Java Objects:
String sqlStr="select 1 from dual where a=b";
Statement statement = CCJSqlParserUtil.parse(sqlStr);
if (statement instanceof Select) {
Select select = (Select) statement;
PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
SelectExpressionItem selectExpressionItem = (SelectExpressionItem) plainSelect.getSelectItems().get(0);
Assertions.assertEquals( new LongValue(1), selectExpressionItem.getExpression());
Table table = (Table) plainSelect.getFromItem();
Assertions.assertEquals("dual", table.getName());
EqualsTo equalsTo = (EqualsTo) plainSelect.getWhere();
Column a = (Column) equalsTo.getLeftExpression();
Column b = (Column) equalsTo.getRightExpression();
Assertions.assertEquals("a", a.getColumnName());
Assertions.assertEquals("b", b.getColumnName());
}
Traverse the Java Object Tree using the Visitor Patterns:
// Define an Expression Visitor reacting on any Expression
// Overwrite the visit() methods for each Expression Class
ExpressionVisitorAdapter expressionVisitorAdapter = new ExpressionVisitorAdapter() {
public void visit(EqualsTo equalsTo) {
equalsTo.getLeftExpression().accept(this);
equalsTo.getRightExpression().accept(this);
}
public void visit(Column column) {
System.out.println("Found a Column " + column.getColumnName());
}
};
// Define a Select Visitor reacting on a Plain Select invoking the Expression Visitor on the Where Clause
SelectVisitorAdapter selectVisitorAdapter = new SelectVisitorAdapter() {
@Override
public void visit(PlainSelect plainSelect) {
plainSelect.getWhere().accept(expressionVisitorAdapter);
}
};
// Define a Statement Visitor for dispatching the Statements
StatementVisitorAdapter statementVisitor = new StatementVisitorAdapter() {
public void visit(Select select) {
select.getSelectBody().accept(selectVisitorAdapter);
}
};
String sqlStr="select 1 from dual where a=b";
Statement stmt = CCJSqlParserUtil.parse(sqlStr);
// Invoke the Statement Visitor
stmt.accept(statementVisitor);
Unit Test 单元测试
单元测试的重点是针对功能进行测试,验证行为的正确性. 这里使用 junit5
进行单元测试 , 使用 assertj
进行测试后的断言.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.23.1</version>
<scope>test</scope>
</dependency>
针对各种语法的 SQL
进行单元测试, 大致的涵盖范围是 SELECT
, INSERT
, UPDATE
, DELETE
. 还包括 JOIN
子句 , Sub Query
子句等. 但是依然不敢保证以及涵盖了所有的标准语法情况,之后会补充完善.
Benchmark Test 基础测试
为了保障开源项目的质量,在保障正确性的基础上还需要对性能提供保障. 所以进行了 Benchmark Test 来发现代码中潜在存在的问题.
Benchmark Test(基准测试)是一种常用于评估计算机性能和稳定性的测试方法。Benchmark Test 通过运行一组标准化测试来模拟计算机在不同负载下的性能表现,可以测量各种硬件和软件组件(如 CPU、内存、磁盘、网络等)的性能指标。
通常,Benchmark Test 由一些常见的、严谨的、可以重复的测试用例组成,这些用例反映了计算机在典型工作负载下的性能表现。测试过程会记录数据,如启动时间、读取和写入速度、响应时间等,并将这些数据与其他系统进行对比,以确定计算机在相应方面的表现。结果可能会绘制成图表以便直观展示。
使用 Benchmark Test 可以帮助用户评估计算机硬件和软件的性能,了解它们在处理某些任务时的表现,并帮助选择合适的设备或软件。同时,Benchmark Test 也有助于开发人员测试和优化代码,提高应用程序的性能。
需要注意的是,Benchmark Test 只是评估计算机在特定负载下的性能,因此在实际应用中,可能会因为其他因素而导致表现不同。因此,在进行 Benchmark Test 时,需要注意测试环境的设置、测试用例的选择、数据的准确性等,以确保测试结果的可靠性和有效性。
在进行 Benchmark Test 时使用了 Java Microbenchmark Harness
(JMH
) . 为了编写的单元测试代码可以重复使用的目的,采用了 junit5
和 JMH
测试方法结合的方式.
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.36</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.36</version>
<scope>test</scope>
</dependency>
在单元测试类中提供了一个 testBenchmark
方法, 在该方法内进行 JMH
的基本配置.
在每个单元测试方法上加上 JMH
注解,用来规定 JMH
对该方法的测试细节.
@Benchmark
@Warmup(iterations = 5, time = 5, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 200, time = 5, timeUnit = TimeUnit.MILLISECONDS)
@CompilerControl(CompilerControl.Mode.INLINE)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
在运行 Benchmark Test 完成后, 会在项目根路径下得到 jmh-result.json
和 JSqlParser_Benchmark.json
两个测试结果文件, 这里我指定 JSON
格式输出, 还支持 TEST
, CSV
, SCSV
, LATEX
格式文件.
jmh-result.json
文件记录了对每一个方法测试的参数和结果
JSqlParser_Benchmark.json
文件记录了对每一个方法测试的参数和结果
直接看这两个文件可能不是太友好,推荐两个个可视化工具可以将测试结果文件以图标的形式展示.
平均时间
采样时间
单次操作时间
吞吐量
我们的单元测试遵循规范,无需依赖任何外部引入,在任何地方任何时候都可以重复执行.
每日英语学习
What's up buddy?
You made it.
Another day, one more done.
You should be proud of yourself.
You really should.
I'm proud of you.
I konw you got a lot going on.
I know you got a lot of shit on your shoulders.
You got a lot of shit on your chest.
Don't stop. You can't quit now.
You come too far man.
Your heart's a warrior.
You're stronger than you think.
You made it through all them other bad days,and I know these days they all blend together.
Yesterday feels like today, and tomorrow's gonna feel like yesterday.
It's kind a just the way it goes, there's only one thing you can do,you gotta change tomorrow , you gotta be better than you were today.
Don't forget, your future you needs you,your past you doesn't,you'll be alright, i know you will, you always are.
Make sure you come back.
DevX
会持续分享有趣的技术和见闻,如果你觉得本文对你有帮助希望你可以分享给更多的朋友看到。该文章会同步在微信公众号 【DevXJava】, 方便在微信客户端阅读。