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); } }