JavaでSQLを解析する：JSqlParserの使い方

# JavaでSQLを解析する：JSqlParserの使い方

## はじめに
JavaでSQL文を解析したり操作したりする必要がある場合、JSqlParserは非常に便利なライブラリです。
本記事では、JSqlParserの基本的な使い方や、実際のユースケースを紹介します。

## JSqlParserとは？
JSqlParserは、SQL文をJavaオブジェクトとして解析するためのオープンソースライブラリです。
このライブラリを使用すると、SQL文を構文解析し、構文ツリーを操作することができます。

主な特徴
- SQL文の解析と構文ツリーの生成
- SQL文の変更や再構築
- SELECT、INSERT、UPDATE、DELETEなどの主要なSQL文に対応

## インストール方法
Mavenを使用してプロジェクトにJSqlParserを追加するには、以下の依存関係をpom.xmlに追加します。

```xml
<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>4.6</version>
</dependency>
```

Gradleを使用している場合は、以下をbuild.gradleに追加します。

```gradle
implementation 'com.github.jsqlparser:jsqlparser:4.6'
```

## 基本的な使い方

### SQL文の解析

以下は、SQL文を解析して構文ツリーを生成する基本的な例です。

```java
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.select.Select;

public class JSqlParserExample {
    public static void main(String[] args) {
        String sql = "SELECT id, name FROM users WHERE age > 30";

        try {
            // SQL文を解析
            Statement statement = CCJSqlParserUtil.parse(sql);

            // SELECT文の場合
            if (statement instanceof Select) {
                Select selectStatement = (Select) statement;
                System.out.println("Parsed SQL: " + selectStatement);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

### SQL内のパーツを取り出す

`ExpressionDeParser`を利用すると、SQL文字列を解析し、たとえばカラム情報を取得するといったこともできます。
以下のコードは、SQL文字列を解析し、カラム情報をリストとして返すメソッドの実装例です。

```java
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.JSQLParserException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

public class SqlColumnParser {

    /**
     * SQL文字列を分割して、各引数でカラムになっているものをList<Column>に格納する
     *
     * @param colVal SQL文字列
     * @return カラムオブジェクトのList
     */
    static List<Column> createColumnArgList(String sql) {
        try {
            // SQL文字列を解析してExpressionオブジェクトを生成
            Expression expression = CCJSqlParserUtil.parseExpression(sql);

            // カラム情報を格納するリスト
            List<Column> columnParts = new ArrayList<>();

            // ExpressionDeParserを使用してカラムを抽出
            ExpressionDeParser expressionDeParser = new ExpressionDeParser() {
                @Override
                public void visit(Column column) {
                    // テーブル情報がないカラムはスキップ
                    if (Objects.isNull(column.getTable())) {
                        return;
                    }
                    // カラムをリストに追加
                    columnParts.add(column);
                }
            };

            // Expressionを解析
            expression.accept(expressionDeParser);

            return columnParts;
        } catch (JSQLParserException e) {
            e.printStackTrace();
        }
        return Collections.emptyList();
    }
}
```

#### 実行例

```java
String sql = "table1.column1 + table2.column2";
List<Column> columns = SqlColumnParser.createColumnArgList(sql);
columns.forEach(column -> System.out.println(column.getColumnName()));
```

出力結果
```text
column1
column2
```

#### 応用例

- カラム名の正規化: SQL文中のカラム名を特定の形式に変換する
- カラムの依存関係解析: SQL文に含まれるカラムの依存関係を解析する
- SQL文の動的生成: 抽出したカラム情報を基に、新しいSQL文を動的に生成する

### 条件式を解析する

`ExpressionDeParser`を使用すると、ANDやORといった論理演算子を含む複雑な条件式を簡単に解析できます。
以下のコードは、SQL文の条件式を解析し、ANDやORごとに分解して出力する例です。

```java
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.util.deparser.ExpressionDeParser;

public class LogicalExpressionParser {

    public static void main(String[] args) {
        String sql = "SELECT * FROM users WHERE age > 30 AND (city = 'Tokyo' OR salary > 5000)";

        try {
            // WHERE句の条件式を解析
            Expression whereExpression = CCJSqlParserUtil.parseCondExpression(sql);

            // ExpressionDeParserを使用して条件式を解析
            StringBuilder buffer = new StringBuilder();
            ExpressionDeParser expressionDeParser = new ExpressionDeParser() {
                @Override
                public void visit(AndExpression andExpression) {
                    System.out.println("Found AND condition: " + andExpression);
                    super.visit(andExpression);
                }

                @Override
                public void visit(OrExpression orExpression) {
                    System.out.println("Found OR condition: " + orExpression);
                    super.visit(orExpression);
                }
            };

            // 条件式を解析して出力
            expressionDeParser.setBuffer(buffer);
            whereExpression.accept(expressionDeParser);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

#### 実行例

以下のSQL文を入力として渡した場合の動作を確認します。

```java
String sql = "SELECT * FROM users WHERE age > 30 AND (city = 'Tokyo' OR salary > 5000)";
```

出力結果
```text
Found AND condition: (age > 30 AND (city = 'Tokyo' OR salary > 5000))
Found OR condition: (city = 'Tokyo' OR salary > 5000)
```

#### 応用例

- 条件式の再構築: 条件式を解析した後、特定の条件を追加・削除して新しいSQL文を生成する
- 条件式の検証: 条件式に特定のカラムや値が含まれているかを検証する
- 条件式の最適化: 冗長な条件式を簡略化する

## 注意点

JSqlParserはSQL文の構文解析に特化しており、SQL文の実行はサポートしていません。
一部のSQL構文やデータベース固有の拡張には対応していない場合があります。

## まとめ

JSqlParserは、SQL文を解析して操作するための強力なツールです。
人間が読解するのに力が必要なSQLも、JSqlParserを利用するとかなり楽に読解できるようになります。
SQL文の解析が必要な場合にぜひ活用してみてください。