✅前言

​ 在某些场景中,我们可能需要在Mybatis拦截器中,根据某个条件,需要对原生SQL中的表名进行替换,从而适配不同的业务需求(比如,查询当前、当月等)。此时,就可以利用 jsqlparser 来优雅的进行替换。

先看一张select的示例(看不清图片可右键 “在新标签页中打开”):

基于新旧表名的映射关系:

1
2
3
4
5
6
7
8
Map<String, String> replacements = Map.of(
"example_table", "新的_example_table",
"nested_example_table", "新的_nested_example_table",
"main_tab_2", "新的_main_tab_2",
"level3", "新的_level3",
"left_table", "新的_left_table",
"table_sec", "新的_table_sec"
);
image-20250313192802306

分割线

0. 环境及依赖说明

  1. JDK 版本 :jdk11

  2. jsqlparser 版本

    1
    2
    3
    4
    5
    <dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>4.7</version>
    </dependency>

1. 具体代码实现

继承 TablesNamesFinder 类,覆写其中【增、删、改、查】相关方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

/**
* 替换Sql表名
*/
public class TableNamesReplacer extends TablesNamesFinder {

/**
* 所有涉及的表名
*/
private List<String> tables;

/**
* 新旧表名映射 Key--原来的表名 Value--新表名
*/
private final Map<String, String> replaceNameMappings;

/**
* 在执行Drop table 时,是否加上 <code> if exists </code> 判断
*/
private final boolean ifExistsWhenDrop;

public TableNamesReplacer(Map<String, String> replaceNameMappings) {
this(replaceNameMappings, true);
}

public TableNamesReplacer(Map<String, String> replaceNameMappings, boolean ifExistsWhenDrop) {
this.replaceNameMappings = replaceNameMappings;
this.ifExistsWhenDrop = ifExistsWhenDrop;
}

public String generateNewSqlStr(Statement statement) {
this.getTables(statement);
return statement.toString();
}

@Override
public void visit(Table tableName) {
String tableWholeName = tableName.getFullyQualifiedName();
if (!this.tables.contains(tableWholeName)) {
this.tables.add(tableWholeName);
}
}

@Override
public Set<String> getTables(Statement statement) {
this.init();
statement.accept(this);
if (CollectionUtils.isEmpty(this.tables)) {
return new HashSet<>();
}
return new HashSet<>(this.tables);
}

protected void init() {
this.tables = new ArrayList<>();
}

/**
* 处理 select 语句
*/
@Override
public void visit(PlainSelect plainSelect) {
if (plainSelect.getSelectItems() != null) {
for (SelectItem<?> item : plainSelect.getSelectItems()) {
item.accept(this);
}
}
FromItem selectFromItem = plainSelect.getFromItem();
if (selectFromItem != null) {
selectFromItem.accept(this);
if (selectFromItem instanceof Table) {
Table tab = (Table) selectFromItem;
if (replaceNameMappings.containsKey(tab.getName())) {
tab.setName(replaceNameMappings.get(tab.getName()));
}
}
}

if (plainSelect.getJoins() != null) {
for (Join join : plainSelect.getJoins()) {
Table fromItem = (Table)join.getFromItem();
String name = fromItem.getName();
fromItem.setName(replaceNameMappings.getOrDefault(name, name));
}
}

Expression selectWhere = plainSelect.getWhere();
if (selectWhere != null) {
selectWhere.accept(this);
}

if (plainSelect.getOracleHierarchical() != null) {
plainSelect.getOracleHierarchical().accept(this);
}
}

/**
* 处理 insert 语句
*/
@Override
public void visit(Insert insert) {
Table table = insert.getTable();
String tableName = table.getName();
if (replaceNameMappings.containsKey(tableName)) {
table.setName(replaceNameMappings.get(tableName));
}
insert.setTable(table);
}

/**
* 处理 update 语句
*/
@Override
public void visit(Update update) {
Table updateTable = update.getTable();
String name = updateTable.getName();
if (replaceNameMappings.containsKey(name)) {
updateTable.setName(replaceNameMappings.get(name));
}
update.setTable(updateTable);
List<Join> joins = update.getStartJoins();
if (CollectionUtils.isNotEmpty(joins)) {
for (Join join : joins) {
Table fromItem = (Table)join.getFromItem();
String joinName = fromItem.getName();
if (replaceNameMappings.containsKey(joinName)) {
fromItem.setName(replaceNameMappings.get(joinName));
}
}
}
Expression where = update.getWhere();
if (Objects.nonNull(where)) {
where.accept(this);
}

OracleHint oracleHint = update.getOracleHint();
if (Objects.nonNull(oracleHint)) {
oracleHint.accept(this);
}
}

/**
* 处理 delete 语句
*/
@Override
public void visit(Delete delete) {
Table deleteTable = delete.getTable();
String delTableName = deleteTable.getName();
if (replaceNameMappings.containsKey(delTableName)) {
deleteTable.setName(replaceNameMappings.get(delTableName));
}
List<Join> joins = delete.getJoins();
if (CollectionUtils.isNotEmpty(joins)) {
for (Join join : joins) {
Table fromItem = (Table)join.getFromItem();
String joinName = fromItem.getName();
if (replaceNameMappings.containsKey(joinName)) {
fromItem.setName(replaceNameMappings.get(joinName));
}
}
}
Expression where = delete.getWhere();
if (Objects.nonNull(where)) {
where.accept(this);
}

OracleHint oracleHint = delete.getOracleHint();
if (Objects.nonNull(oracleHint)) {
oracleHint.accept(this);
}
}

/**
* 处理 drop 语句
*/
@Override
public void visit(Drop drop) {
Table droTable = drop.getName();
String droTableName = droTable.getName();
// 为true,会自动加上 IF EXISTS, 如:
// DROP table IF EXISTS example_table
drop.setIfExists(ifExistsWhenDrop);
droTable.setName(replaceNameMappings.getOrDefault(droTableName, droTableName));
}

/**
* 处理 select 语句
*/
@Override
public void visit(Truncate truncate) {
Table truncateTable = truncate.getTable();
String truncateTableName = truncateTable.getName();
truncateTable.setName(replaceNameMappings.getOrDefault(truncateTableName, truncateTableName));
}
}

2. 测试代码

  • 🔔主程序代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    public class SqlParserTest {

    public static void main(String[] args) {
    String originalSql = "SELECT * FROM example_table etAsn, main_tab_2 mt2 left join left_table lt, table_sec tsc WHERE column IN (SELECT other_column FROM nested_example_table xxx where id in (select id from level3 yyy))";
    // String originalSql = "INSERT into example_table values (1,2,3)";
    // String originalSql = "INSERT into example_table select c1, c2 from table_sec"; // 这种无法替换 table_sec
    // String originalSql = "update example_table tt left join table_sec lxx set id=1 where id in (select id from nested_example_table xx)";
    // String originalSql = "delete from example_table a right join table_sec xx where id in (select id from nested_example_table)";
    // String originalSql = "drop table example_table";
    // String originalSql = "TRUNCATE table example_table";
    Map<String, String> replacements = Map.of(
    "example_table", "新的_example_table",
    "nested_example_table", "新的_nested_example_table",
    "main_tab_2", "新的_main_tab_2",
    "level3", "新的_level3",
    "left_table", "新的_left_table",
    "table_sec", "新的_table_sec"
    );

    TableNamesReplacer nameReplacer = new TableNamesReplacer(replacements);
    System.out.println("原来的SQL是:");
    System.out.println(originalSql);
    try {
    Statement statement = CCJSqlParserUtil.parse(originalSql);
    String newSqlStr = nameReplacer.generateNewSqlStr(statement);
    System.out.println("改变后的SQL是:");
    System.err.println(newSqlStr);
    } catch (JSQLParserException e) {
    e.printStackTrace();
    }
    }
    }
  • 🔍测试 select 语句

    运行结果:

    1
    2
    3
    4
    原来的SQL是:
    SELECT * FROM example_table etAsn, main_tab_2 mt2 left join left_table lt, table_sec tsc WHERE column IN (SELECT other_column FROM nested_example_table xxx where id in (select id from level3 yyy))
    改变后的SQL是:
    SELECT * FROM 新的_example_table etAsn, 新的_main_tab_2 mt2 LEFT JOIN 新的_left_table lt, 新的_table_sec tsc WHERE column IN (SELECT other_column FROM 新的_nested_example_table xxx WHERE id IN (SELECT id FROM 新的_level3 yyy))
  • 📝测试 insert 语句

    运行结果1:

    1
    2
    3
    4
    原来的SQL是:
    INSERT into example_table values (1,2,3)
    改变后的SQL是:
    INSERT INTO 新的_example_table VALUES (1, 2, 3)

    yun

    运行结果2:

    注意: 后面子查询的表不会被替换
    1
    2
    3
    4
    原来的SQL是:
    INSERT into example_table select c1, c2 from table_sec
    改变后的SQL是:
    INSERT INTO 新的_example_table SELECT c1, c2 FROM table_sec -- 这里注意,table_sec没有被替换
  • 🔄测试 update 语句

    运行结果:

    1
    2
    3
    4
    原来的SQL是:
    update example_table tt left join table_sec lxx set id=1 where id in (select id from nested_example_table xx)
    改变后的SQL是:
    UPDATE 新的_example_table tt LEFT JOIN 新的_table_sec lxx SET id = 1 WHERE id IN (SELECT id FROM 新的_nested_example_table xx)
  • 测试 delete 语句

    运行结果:

    1
    2
    3
    4
    原来的SQL是:
    delete from example_table a right join table_sec xx where id in (select id from nested_example_table)
    改变后的SQL是:
    DELETE FROM 新的_example_table a RIGHT JOIN 新的_table_sec xx WHERE id IN (SELECT id FROM 新的_nested_example_table)
  • 🅾测试 drop 语句

    运行结果:

    1
    2
    3
    4
    5
    原来的SQL是:
    drop table example_table
    改变后的SQL是:
    DROP table IF EXISTS 新的_example_table

  • 测试 truncate 语句

    运行结果:

    1
    2
    3
    4
    原来的SQL是:
    drop table example_table
    改变后的SQL是:
    DROP table IF EXISTS 新的_example_table

说明: 如有问题,请评论区留言,谢谢!