S-JIS[2018-09-29/2018-10-06] 変更履歴

JSqlParser

Javaで使えるJSqlParserのメモ。


概要

JSqlParserは、SQLをパースするライブラリー。
パースされたSQLをVisitorパターンで処理できる。

JDBCのパラメーター「?」や「:name」も識別できる。


インストール

JSqlParserはMavenリポジトリーから取得できる。

build.gradle

〜
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の例

CCJSqlParserUtilを使ってパースすることも出来る。(内部ではCCJSqlParserManagerが呼ばれている)[2018-10-06]

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;

1つのSQL文

	public static void singleStatement() throws JSQLParserException {
		String sql = "select * from dual";
		Statement s = CCJSqlParserUtil.parse(sql);
		System.out.println(s);
	}

複数のSQL文

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

Javaへ戻る / 技術メモへ戻る
メールの送信先:ひしだま