Javaで使えるJSqlParserのメモ。
|
JSqlParserは、SQLをパースするライブラリー。
パースされたSQLをVisitorパターンで処理できる。
JDBCのパラメーター「?」や「:name」も識別できる。
JSqlParserはMavenリポジトリーから取得できる。
〜
repositories {
mavenCentral()
}
dependencies {
// https://mvnrepository.com/artifact/com.github.jsqlparser/jsqlparser
compile group: 'com.github.jsqlparser', name: 'jsqlparser', version: '1.2'
}
import java.io.Reader; import java.io.StringReader; import java.util.List; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.expression.ExpressionVisitorAdapter; import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.expression.JdbcNamedParameter; import net.sf.jsqlparser.expression.JdbcParameter; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.operators.conditional.AndExpression; import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.StatementVisitor; import net.sf.jsqlparser.statement.StatementVisitorAdapter; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; import net.sf.jsqlparser.statement.select.FromItem; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectBody; import net.sf.jsqlparser.statement.select.SelectExpressionItem; import net.sf.jsqlparser.statement.select.SelectItem; import net.sf.jsqlparser.statement.select.SelectItemVisitor; import net.sf.jsqlparser.statement.select.SelectItemVisitorAdapter; import net.sf.jsqlparser.statement.select.SelectVisitor; import net.sf.jsqlparser.statement.select.SelectVisitorAdapter;
public class JSqlParserExample {
public static void main(String... args) throws Exception {
String sql = "select a,b,1,nvl(a,b) from dual where a=? and b=:b";
Statement statement;
try (Reader reader = new StringReader(sql)) {
// 正常終了時はparseの中でReaderのcloseが呼ばれるが、例外発生時は呼ばれない(SQLの文法が変だと例外が発生する)
statement = new CCJSqlParserManager().parse(reader);
}
System.out.println("statement\t" + statement);
StatementVisitor visitor = new MyStatementVisitor();
statement.accept(visitor);
}
static class MyStatementVisitor extends StatementVisitorAdapter {
private SelectVisitor visitor = new MySelectVisitor();
@Override
public void visit(Select select) {
System.out.println("select\t" + select);
SelectBody body = select.getSelectBody();
System.out.println("body\t" + body);
body.accept(visitor);
}
}
static class MySelectVisitor extends SelectVisitorAdapter {
private SelectItemVisitor selectItemVisitor = new MySelectItemVisitor();
private ExpressionVisitor expressionVisitor = new MyExpressionVisitor();
@Override
public void visit(PlainSelect plainSelect) {
System.out.println("plainSelect\t" + plainSelect);
FromItem from = plainSelect.getFromItem();
System.out.println("FromItem=" + from);
List<SelectItem> itemList = plainSelect.getSelectItems();
for (SelectItem item : itemList) {
System.out.println("SelectItem=" + item);
item.accept(selectItemVisitor);
}
Expression where = plainSelect.getWhere();
System.out.println("where=" + where);
where.accept(expressionVisitor);
}
}
static class MySelectItemVisitor extends SelectItemVisitorAdapter {
private ExpressionVisitor expressionVisitor = new MyExpressionVisitor();
// *
@Override
public void visit(AllColumns columns) {
System.out.println("AllColumns\t" + columns);
}
// t.*
@Override
public void visit(AllTableColumns columns) {
System.out.println("AllTableColumns\t" + columns);
}
// 通常のカラム
@Override
public void visit(SelectExpressionItem item) {
System.out.println("SelectExpressionItem\t" + item);
Alias alias = item.getAlias();
System.out.println("alias=" + alias);
Expression expression = item.getExpression();
System.out.println("expression=" + expression);
expression.accept(expressionVisitor);
}
}
static class MyExpressionVisitor extends ExpressionVisitorAdapter {
// 通常のカラム(カラム名)
@Override
public void visit(Column column) {
System.out.println("column=" + column.getColumnName() + "\t" + column.getFullyQualifiedName());
}
// 定数(long)
@Override
public void visit(LongValue value) {
System.out.println("longValue=" + value.getValue());
}
// 関数
@Override
public void visit(Function function) {
System.out.println("Function\t" + function);
String name = function.getName();
System.out.println("name=" + name);
ExpressionList parameters = function.getParameters();
System.out.println("parameters=" + parameters);
}
// AND
@Override
public void visit(AndExpression expr) {
System.out.println("and");
super.visit(expr);
}
// =(等値比較)
@Override
public void visit(EqualsTo expr) {
System.out.println("=");
super.visit(expr);
}
// ?
@Override
public void visit(JdbcParameter parameter) {
System.out.println("JdbcParameter\t" + parameter.getIndex());
}
// :name
@Override
public void visit(JdbcNamedParameter parameter) {
System.out.println("JdbcNamedParameter\t" + parameter.getName());
}
}
}
CCJSqlParserUtilを使ってパースすることも出来る。(内部ではCCJSqlParserManagerが呼ばれている)[2018-10-06]
import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.parser.CCJSqlParserUtil;
public static void singleStatement() throws JSQLParserException {
String sql = "select * from dual";
Statement s = CCJSqlParserUtil.parse(sql);
System.out.println(s);
}
public static void multiStatement() throws JSQLParserException {
String sql = "select * from dual;\n"
+ "select * from t";
Statements ss = CCJSqlParserUtil.parseStatements(sql);
System.out.println(ss);
}
パースして得られるStatementには、その文の位置情報は含まれていない。
ノードから取得できるトークンに位置情報が入っている。
import net.sf.jsqlparser.parser.Node; import net.sf.jsqlparser.parser.SimpleNode; import net.sf.jsqlparser.parser.Token;
public static void node() throws JSQLParserException {
String sql = "select * from dual";
Node node = CCJSqlParserUtil.parseAST(sql);
SimpleNode s = (SimpleNode) node;
Token first = s.jjtGetFirstToken();
Token last = s.jjtGetLastToken();
System.out.printf("%d:%d-%d:%d%n", first.beginLine, first.beginColumn, last.endLine, last.endColumn);
}
Nodeはインターフェースで、具象クラスはSimpleNodeのみ。
SimpleNodeのjjtGetValueメソッドでStatement(パースされた各要素のオブジェクト)が取れる。(nullの場合もある)
NodeのjjtGetNumChildrenメソッドで子ノードの個数、jjtGetChildメソッドで子ノードが取れる。
Tokenにはnextフィールドがあり、それを辿ると全トークンが取得できる。
また、TokenのspecialTokenフィールドから、そのトークンの前にあったコメント(SPECIAL_TOKEN)が取得できる。(無い場合はnull)
public static void specialToken() throws JSQLParserException {
String sql = "select a, --aaa\n"
+ "b --bbb\n"
+ "from dual";
SimpleNode node = (SimpleNode) CCJSqlParserUtil.parseAST(sql);
for (Token token = node.jjtGetFirstToken(); token != null; token = token.next) {
Token s = token.specialToken;
if (s != null) {
System.out.printf("%2d:%2d-%2d:%2d\t%s%n", s.beginLine, s.beginColumn, s.endLine, s.endColumn, s.image);
}
System.out.println(token.image);
}
}