Java手写一个简单的sql解析执行器
一,实现步骤
实现一个简单的 SQL 查询引擎需要完成以下几个主要功能:
- SQL 解析:将用户输入的 SQL 语句解析成可执行的结构。
- 数据存储:存储数据,可以使用内存数据结构(如
HashMap或ArrayList)模拟数据库表。 - 查询执行:根据解析后的 SQL 语句,从数据存储中查询数据。
- 结果输出:将查询结果输出给用户。
以下是一个简单的实现示例,支持基本的 SELECT 查询,包括字段选择、表名和简单的 WHERE 条件。
1,数据存储
首先,我们需要一个简单的数据存储结构。这里使用
HashMap来模拟一个表,其中键是列名,值是列数据。2,SQL 解析
接下来,实现一个简单的 SQL 解析器,解析
SELECT和WHERE语句。3,查询执行
根据解析后的 SQL 查询,从数据存储中提取数据。
1,实现数据存储和SQL执行器
上面的一、三两步代码归为一处,数据存储和SQL执行器
package com.example.demo.controller;
import java.util.*;
/**
* 数据存储
* 首先,我们需要一个简单的数据存储结构。这里使用 HashMap 来模拟一个表,其中键是列名,值是列数据。
*/
public class SimpleSQLQueryEngine {
// 模拟数据库表
private Map<String, List<String>> table;
public SimpleSQLQueryEngine() {
table = new HashMap<>();
}
// 向表中添加数据
public void addColumn(String columnName, List<String> data) {
table.put(columnName, data);
}
// 获取表结构
public Map<String, List<String>> getTable() {
return table;
}
//SQL执行器
public List<Map<String, String>> executeQuery(SQLParser.SQLQuery query) {
String[] fields = query.getSelectFields().split(",\\s*");
String tableName = query.getTableName();
String whereCondition = query.getWhereCondition();
List<Map<String, String>> results = new ArrayList<>();
// 遍历每一行数据
int rowCount = table.values().iterator().next().size();
for (int i = 0; i < rowCount; i++) {
Map<String, String> row = new HashMap<>();
boolean matchWhere = true;
for (String field : fields) {
if (table.containsKey(field)) {
row.put(field, table.get(field).get(i));
} else {
throw new IllegalArgumentException("Column not found: " + field);
}
}
// 检查 WHERE 条件
if (whereCondition != null && !whereCondition.isEmpty()) {
String[] conditionParts = whereCondition.split("\\s+");
if (conditionParts.length != 3) {
throw new IllegalArgumentException("Invalid WHERE condition");
}
String column = conditionParts[0];
String operator = conditionParts[1];
String value = conditionParts[2];
if (!table.containsKey(column)) {
throw new IllegalArgumentException("Column not found in WHERE condition: " + column);
}
String cellValue = table.get(column).get(i);
switch (operator) {
case "=":
matchWhere = cellValue.equals(value);
break;
case ">":
matchWhere = Integer.parseInt(cellValue) > Integer.parseInt(value);
break;
case "<":
matchWhere = Integer.parseInt(cellValue) < Integer.parseInt(value);
break;
default:
throw new IllegalArgumentException("Unsupported operator: " + operator);
}
}
if (matchWhere) {
results.add(row);
}
}
return results;
}
}
2,实现SQL解析器
package com.example.demo.controller;
import java.util.*;
import java.util.regex.*;
/**
* SQL 解析
* 接下来,实现一个简单的 SQL 解析器,解析 SELECT 和 WHERE 语句。
*/
public class SQLParser {
private static final Pattern SELECT_PATTERN = Pattern.compile(
"\\bSELECT\\b\\s*(.*?)\\s*\\bFROM\\b\\s*(\\w+)(\\s*\\bWHERE\\b\\s*(.*))?",
Pattern.CASE_INSENSITIVE
);
public static SQLQuery parse(String sql) {
Matcher matcher = SELECT_PATTERN.matcher(sql.trim());
if (!matcher.matches()) {
throw new IllegalArgumentException("Invalid SQL query");
}
String selectFields = matcher.group(1).trim();
String tableName = matcher.group(2).trim();
String whereCondition = matcher.group(4);
return new SQLQuery(selectFields, tableName, whereCondition);
}
public static class SQLQuery {
private String selectFields;
private String tableName;
private String whereCondition;
public SQLQuery(String selectFields, String tableName, String whereCondition) {
this.selectFields = selectFields;
this.tableName = tableName;
this.whereCondition = whereCondition;
}
public String getSelectFields() {
return selectFields;
}
public String getTableName() {
return tableName;
}
public String getWhereCondition() {
return whereCondition;
}
}
}
3,主程序执行SQL测试
将上述部分组合起来,实现一个简单的 SQL 查询引擎
package com.example.demo.controller;
import java.util.*;
/**
* 实现一个简单的 SQL 查询引擎需要完成以下几个主要功能:
* 1,SQL 解析:将用户输入的 SQL 语句解析成可执行的结构。
* 2,数据存储:存储数据,可以使用内存数据结构(如 HashMap 或 ArrayList)模拟数据库表。
* 3,查询执行:根据解析后的 SQL 语句,从数据存储中查询数据。
* 4,结果输出:将查询结果输出给用户。
* 以下是一个简单的实现示例,支持基本的 SELECT 查询,包括字段选择、表名和简单的 WHERE 条件。
*
* 主程序
* 将上述部分组合起来,实现一个简单的 SQL 查询引擎。
*/
public class Main {
public static void main(String[] args) {
// 创建查询引擎
SimpleSQLQueryEngine engine = new SimpleSQLQueryEngine();
// 添加数据
engine.addColumn("name", Arrays.asList("Alice", "Bob", "Charlie"));
engine.addColumn("age", Arrays.asList("25", "30", "35"));
// 输入 SQL 查询
String sql = "SELECT name, age FROM table WHERE age > 25";
// 解析 SQL
SQLParser.SQLQuery query = SQLParser.parse(sql);
// 执行查询
List<Map<String, String>> results = engine.executeQuery(query);
// 输出结果
for (Map<String, String> row : results) {
System.out.println(row);
}
}
}
4,运行结果
对于输入的 SQL 查询
SELECT name, age FROM table WHERE age > 25
程序输出
{name=Bob, age=30}
{name=Charlie, age=35}
5,扩展建议
- 支持更多 SQL 语法:目前只支持简单的
SELECT和WHERE,可以扩展支持INSERT、UPDATE、DELETE等操作。 - 优化性能:对于大数据量,可以考虑使用更高效的数据结构或索引机制。
- 错误处理:增加更详细的错误处理和用户提示。
- 支持更多数据类型:目前只支持字符串和整数,可以扩展支持浮点数、日期等。
这个简单的提供了一个基础框架,可以根据需求进一步扩展和优化。
二,知识点补充
Pattern.compile 是 Java 中正则表达式库 java.util.regex 的一个重要方法,用于将字符串形式的正则表达式编译成 Pattern 对象。Pattern 对象是正则表达式的编译表示形式,可以用于匹配字符串。
1. Pattern.compile 的基本用法
方法签名
public static Pattern compile(String regex)
- 参数:
regex:字符串形式的正则表达式。
- 返回值:
- 返回一个
Pattern对象,表示编译后的正则表达式。
- 返回一个
常用的正则表达式修饰符
Pattern.compile 还可以接受一个修饰符参数,用于指定正则表达式的匹配模式。常用的修饰符包括:
Pattern.CASE_INSENSITIVE:忽略大小写。Pattern.MULTILINE:多行模式,^和$匹配每一行的开始和结束。Pattern.DOTALL:.匹配任何字符,包括换行符。Pattern.UNICODE_CASE:启用 Unicode 感知的大小写匹配。
public static Pattern compile(String regex, int flags)
2. 示例代码
示例 1:简单匹配
以下代码演示了如何使用 Pattern.compile 来匹配字符串中的数字。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class PatternCompileExample {
public static void main(String[] args) {
// 编译正则表达式,匹配数字
Pattern pattern = Pattern.compile("\\d+");
// 创建 Matcher 对象,用于匹配字符串
Matcher matcher = pattern.matcher("Hello 123, this is 456");
// 查找匹配项
while (matcher.find()) {
System.out.println("Found number: " + matcher.group());
}
}
}
输出结果:
Found number: 123
Found number: 456
示例 2:忽略大小写
以下代码演示了如何使用 Pattern.CASE_INSENSITIVE 修饰符来忽略大小写。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class CaseInsensitiveExample {
public static void main(String[] args) {
// 编译正则表达式,忽略大小写
Pattern pattern = Pattern.compile("hello", Pattern.CASE_INSENSITIVE);
// 创建 Matcher 对象
Matcher matcher = pattern.matcher("Hello world");
// 查找匹配项
if (matcher.find()) {
System.out.println("Found match: " + matcher.group());
}
}
}
输出结果:
Found match: Hello
示例 3:多行模式
以下代码演示了如何使用 Pattern.MULTILINE 修饰符来匹配多行字符串。
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MultilineExample {
public static void main(String[] args) {
// 编译正则表达式,多行模式
Pattern pattern = Pattern.compile("^line", Pattern.MULTILINE);
// 创建 Matcher 对象
Matcher matcher = pattern.matcher("line 1\nline 2\nanother line");
// 查找匹配项
while (matcher.find()) {
System.out.println("Found match: " + matcher.group());
}
}
}
输出结果:
Found match: line
Found match: line
3. Pattern 和 Matcher 的常用方法
Pattern.compile(String regex):编译正则表达式。Pattern.matcher(CharSequence input):创建一个Matcher对象,用于匹配输入字符串。Matcher.find():查找输入字符串中下一个匹配项。Matcher.matches():检查整个输入字符串是否匹配正则表达式。Matcher.group():获取匹配的结果字符串。Matcher.group(int group):获取指定捕获组的匹配结果。
4. 注意事项
- 性能优化:
- 如果需要多次匹配同一个正则表达式,建议提前编译
Pattern对象,而不是每次都调用Pattern.compile。 - 编译后的
Pattern对象可以多次复用。
- 如果需要多次匹配同一个正则表达式,建议提前编译
- 特殊字符转义:
- 正则表达式中的一些特殊字符(如
.、*、?、(、)等)需要使用反斜杠\转义。 - 如果需要匹配反斜杠本身,需要使用
\\。
- 正则表达式中的一些特殊字符(如
- 异常处理:
- 如果正则表达式语法错误,
Pattern.compile会抛出PatternSyntaxException。建议在实际应用中捕获并处理该异常。
- 如果正则表达式语法错误,
5. Pattern.compile 总结
Pattern.compile 是 Java 中处理正则表达式的基础方法,通过它可以将字符串形式的正则表达式编译成 Pattern 对象,进而使用 Matcher 对象进行匹配操作。掌握其用法可以方便地实现复杂的字符串匹配和处理逻辑。
6. 上面的正则表达式逐步解析
这句代码的作用是使用 Java 的正则表达式库 java.util.regex 来编译一个正则表达式模式,用于匹配简单的 SQL SELECT 查询语句。它通过正则表达式提取查询中的关键部分,如 SELECT 字段、FROM 表名和可选的 WHERE 条件。同时,它还设置了 Pattern.CASE_INSENSITIVE 标志,使匹配过程忽略大小写。
让我们逐步解析这句代码:
1. 正则表达式部分
"\\bSELECT\\b\\s*(.*?)\\s*\\bFROM\\b\\s*(\\w+)(\\s*\\bWHERE\\b\\s*(.*))?"
1.1 \bSELECT\b
\b是单词边界(word boundary)的正则表达式符号,表示匹配单词的开始或结束。SELECT是要匹配的单词。- 这部分的作用是确保匹配到独立的单词
SELECT,而不是单词的一部分(例如SELECTED)。
1.2 \s*
\s表示匹配任意空白字符(包括空格、制表符、换行符等)。*表示匹配前面的字符(这里是空白字符)0次或多次。- 这部分的作用是允许在
SELECT和后面的字段之间有任意数量的空白字符。
1.3 (.*?)
()是捕获组(capturing group),用于捕获匹配的部分,以便后续可以通过Matcher.group()方法提取出来。.表示匹配任意单个字符(除换行符外)。*表示匹配前面的字符(这里是任意字符)0次或多次。?表示非贪婪匹配(non-greedy),即尽可能少地匹配字符。- 这部分的作用是捕获
SELECT和FROM之间的字段列表(例如name, age)。
1.4 \bFROM\b
- 与
\bSELECT\b类似,匹配独立的单词FROM。
1.5 \s*(\w+)
\s*同前面的解释,匹配任意数量的空白字符。(\w+):()是捕获组。\w表示匹配任意字母、数字或下划线(即单词字符)。+表示匹配前面的字符(这里是单词字符)1次或多次。
- 这部分的作用是捕获
FROM后面的表名(例如users)。
1.6 (\s*\bWHERE\b\s*(.*))?
()是捕获组。?表示前面的捕获组是可选的,即WHERE条件可以不存在。\s*\bWHERE\b\s*:- 匹配任意数量的空白字符,然后是单词
WHERE,再后面是任意数量的空白字符。
- 匹配任意数量的空白字符,然后是单词
(.*?):- 捕获
WHERE后面的条件部分(例如age > 20)。
- 捕获
- 这部分的作用是捕获可选的
WHERE条件。
2. Pattern.CASE_INSENSITIVE
Pattern.CASE_INSENSITIVE是一个修饰符,表示在匹配过程中忽略大小写。- 这意味着正则表达式会匹配
SELECT、select、SeLeCt等所有大小写变体。
3. 代码的作用
这句代码的作用是编译一个正则表达式模式,用于解析简单的 SQL SELECT 查询语句。它能够提取以下部分:
SELECT后面的字段列表(例如name, age)。FROM后面的表名(例如users)。- 可选的
WHERE条件(例如age > 20)。
4. 示例
假设有一个 SQL 查询:
SELECT name, age FROM users WHERE age > 20
使用这个正则表达式匹配后,可以提取出以下内容:
- 第一个捕获组
(.*?):name, age - 第二个捕获组
(\w+):users - 第三个捕获组
(.*?):age > 20
5. 使用示例
以下是一个简单的使用示例:
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class SQLParserExample {
public static void main(String[] args) {
String sql = "SELECT name, age FROM users WHERE age > 20";
Pattern pattern = Pattern.compile(
"\\bSELECT\\b\\s*(.*?)\\s*\\bFROM\\b\\s*(\\w+)(\\s*\\bWHERE\\b\\s*(.*))?",
Pattern.CASE_INSENSITIVE
);
Matcher matcher = pattern.matcher(sql);
if (matcher.matches()) {
System.out.println("SELECT fields: " + matcher.group(1)); // 输出: name, age
System.out.println("FROM table: " + matcher.group(2)); // 输出: users
System.out.println("WHERE condition: " + matcher.group(4)); // 输出: age > 20
} else {
System.out.println("Invalid SQL query");
}
}
}
6. 正则表达式解析总结
这句代码通过正则表达式解析简单的 SQL SELECT 查询语句,提取关键部分(字段、表名、条件),并忽略大小写。它在实现简单的 SQL 解析器时非常有用。
7. Matcher.group() 的用法
在 Java 的正则表达式中,Matcher.group() 方法用于获取匹配结果中的捕获组(capturing group)内容。捕获组是正则表达式中用圆括号 () 包裹的部分,用于提取匹配的子字符串。
方法签名
public String group(int group)
- 参数:
group:捕获组的索引。0表示整个匹配的字符串,1表示第一个捕获组,2表示第二个捕获组,依此类推。
- 返回值:
- 返回指定捕获组匹配的子字符串。如果没有匹配到,返回
null。
- 返回指定捕获组匹配的子字符串。如果没有匹配到,返回
特殊情况
- 如果调用
group()方法时不传参数(即group()),等同于调用group(0),表示获取整个匹配的字符串。
示例代码
以下是一个完整的示例,展示如何使用 Matcher.group() 方法提取正则表达式中的捕获组内容。
示例代码
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class MatcherGroupExample {
public static void main(String[] args) {
// 定义正则表达式
String regex = "\\bSELECT\\b\\s*(.*?)\\s*\\bFROM\\b\\s*(\\w+)(\\s*\\bWHERE\\b\\s*(.*))?";
// 创建 Pattern 对象
Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
// 待匹配的字符串
String sql = "SELECT name, age FROM users WHERE age > 20";
// 创建 Matcher 对象
Matcher matcher = pattern.matcher(sql);
// 检查是否匹配
if (matcher.matches()) {
// 获取整个匹配的字符串(group 0)
System.out.println("Entire match: " + matcher.group(0));
// 获取第一个捕获组(SELECT 字段)
System.out.println("SELECT fields: " + matcher.group(1));
// 获取第二个捕获组(FROM 表名)
System.out.println("FROM table: " + matcher.group(2));
// 获取第三个捕获组(WHERE 条件)
System.out.println("WHERE condition: " + matcher.group(4));
} else {
System.out.println("No match found");
}
}
}
输出结果
Entire match: SELECT name, age FROM users WHERE age > 20
SELECT fields: name, age
FROM table: users
WHERE condition: age > 20
详细解释
matcher.group(0):- 获取整个匹配的字符串,即整个 SQL 查询语句。
- 在这个例子中,
matcher.group(0)返回"SELECT name, age FROM users WHERE age > 20"。
matcher.group(1):- 获取第一个捕获组的内容,即
SELECT和FROM之间的字段部分。 - 在这个例子中,
matcher.group(1)返回"name, age"。
- 获取第一个捕获组的内容,即
matcher.group(2):- 获取第二个捕获组的内容,即
FROM后面的表名。 - 在这个例子中,
matcher.group(2)返回"users"。
- 获取第二个捕获组的内容,即
matcher.group(4):- 获取第四个捕获组的内容,即
WHERE后面的条件部分。 - 在这个例子中,
matcher.group(4)返回"age > 20"。
- 获取第四个捕获组的内容,即
注意事项
- 捕获组的索引从 1 开始:
group(0)表示整个匹配的字符串。group(1)表示第一个捕获组,group(2)表示第二个捕获组,依此类推。
- 捕获组可能为空:
- 如果某个捕获组没有匹配到任何内容,
group()方法会返回null或空字符串,具体取决于正则表达式的设计。 - 例如,如果 SQL 查询中没有
WHERE条件,matcher.group(4)将返回null。
- 如果某个捕获组没有匹配到任何内容,
Matcher.find()和Matcher.matches()的区别:Matcher.matches():尝试匹配整个输入字符串。Matcher.find():尝试在输入字符串中查找下一个匹配项。- 在使用
Matcher.find()时,每次调用find()都会移动匹配器的游标,因此可以多次调用group()方法来提取多个匹配结果。
总结
Matcher.group() 方法是 Java 正则表达式中非常重要的工具,用于提取匹配的子字符串。通过指定捕获组的索引,可以方便地获取正则表达式中定义的各个部分的内容。
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!

浙公网安备 33010602011771号