DevXRoutingDataSource 的核心功能也是最基础的理念是根据 SQL 语句去进行多数据源的路由, 所以 SQL 解析是其中很重要的一个环节。为了保障开源项目的质量,我们会编写很多的测试代码,从各个角度对代码进行测试。本章内容是关于对 SQL 解析进行测试。
目前提供了两种实现,一种基于 Apache Calcite (https://github.com/apache/calcite) (开发中) , 一种基于 JSqlParser (https://github.com/JSQLParser/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);
单元测试的重点是针对功能进行测试,验证行为的正确性. 这里使用 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 通过运行一组标准化测试来模拟计算机在不同负载下的性能表现,可以测量各种硬件和软件组件(如 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】, 方便在微信客户端阅读。
DevX 不止于技术

原文作者:DevXTalk;如有侵权,请联系管理员删除。