用Antlr4处理sql中的多语言字段
对于一个后端开发来说,在写带多语言字段的sql时,往往需要手动join下多语言表,查询对应内容。 例如现在有两个表
主book表
| 字段 | |
|---|---|
| id | 书id |
| book_name | 书名 |
多语言book_tl表
| 字段 | |
|---|---|
| id | 书id |
| lang | 语言code |
| book_name | 书名 |
当想查询书列表的时候,我们会这样写sql
select
b.id,
bk.book_name
from book b left join book_tl bt on b.id = bk.id and bk.lang = 'en_US'
写多了就觉得烦了,也不利于阅读,还容易漏。 那有没有一种方式,可以自动处理多语言内容? 在xml里只写
select
b.id,
b.book_name
from book b
然后执行的时候自动处理成
select
b.id,
bk.book_name
from book b left join book_tl bt on b.id = bk.id and bk.lang = 'en_US'
答案是可以。
用到的就是antlr4-强大的语法解析工具
关于antlr4的具体内容在这不做展开,可以阅读相关书例如《antlr4实战》
简单的来说,我们给antler4一个sql的语法规则,然后antlr4就能解析sql了,将sql解析未一颗语法树。 然后在遍历树的时候,我们就能做一些事情了,对于本文来说,就是自动给表join上多语言表
下面是主要的过程
首先需要给entity对象加一些元数据,作为后面替换的依据
比如
@Table(name = "student")//表名
@MultiLanguage(name = "student_tl") //多语言表名
public class Student {
private Long id;
@MultiLanguageField //代表这个字段是多语言字段
private String bookName;
.
.
.
}
然后写一个实现BeanPostProcessor的类,在服务启动的时候收集多语言元数据信息 收集到一个map,记录哪些表是多语言表,哪些字段是多语言字段
然后就是antlr4部分 准备一个解析sql的词法、语法规则,使用antlr4,将规则生成相应的java代码
然后就是遍历语法树,遇到表名节点时,通过前面收集的多语言元数据,以及select的列是否包含多语言字段,将join 多语言表的sql插入对应的节点,以及将字段改成多语言表的字段。
public class MultiLangSqlRecognizer extends SelectBaseVisitor<Mdata> {
private final List<Mdata> mdataList = new ArrayList<>();
public String apply(String originSql) throws IOException {
InputStream is = new ByteArrayInputStream(originSql.toUpperCase().getBytes(StandardCharsets.UTF_8));
CharStream stream = CharStreams.fromStream(is);
// 词法分析
SelectLexer lexer = new SelectLexer(stream);
lexer.removeErrorListeners();
CommonTokenStream tokens = new CommonTokenStream(lexer);
// 语法分析
SelectParser parser = new SelectParser(tokens);
parser.removeErrorListeners();
ParseTree ast = parser.selectStatement();
//访问语法树
this.visit(ast);
//处理多语言
List<Pair<Pair<Integer, Integer>, String>> replaceInfos = recognizeLangInfo(mdataList);
if (replaceInfos.size() > 0) {
return handleReplace(originSql, replaceInfos);
} else {
return null;
}
}
通过antlr4,我们将得到一个转换后的sql 最后通过mybatis的拦截器,将这个转换过程应用上,即完成了多余sql的自动装配。