001package com.streamconverter.tools;
002
003import java.sql.Connection;
004import java.sql.DriverManager;
005import java.sql.PreparedStatement;
006import java.sql.ResultSet;
007import java.sql.ResultSetMetaData;
008import java.sql.SQLException;
009import java.util.Scanner;
010
011// import org.h2.tools.Server; // H2のWebサーバー機能は依存関係の問題でコメントアウト
012
013/**
014 * H2データベースの中身を確認するためのツール
015 *
016 * <p>このツールでは以下の方法でH2データベースにアクセスできます: 1. H2 Web Console(ブラウザ経由) 2. 対話型SQLクライアント(コマンドライン) 3.
017 * データベース構造の表示
018 */
019public class DatabaseInspector {
020
021  private static final String DEFAULT_DB_URL = "jdbc:h2:mem:demo;DB_CLOSE_DELAY=-1";
022
023  public static void main(String[] args) {
024    try {
025      System.out.println("=== H2 Database Inspector ===\n");
026
027      // デモ用データベースを初期化
028      initializeDemoDatabase();
029
030      // メニュー表示
031      showMenu();
032
033    } catch (Exception e) {
034      System.err.println("エラーが発生しました: " + e.getMessage());
035      e.printStackTrace();
036    }
037  }
038
039  /** メニューを表示して操作を選択 */
040  private static void showMenu() {
041    Scanner scanner = new Scanner(System.in);
042
043    while (true) {
044      System.out.println("\n--- 操作メニュー ---");
045      System.out.println("1. H2 Web Console を起動");
046      System.out.println("2. テーブル一覧を表示");
047      System.out.println("3. テーブルのデータを表示");
048      System.out.println("4. カスタムSQLクエリを実行");
049      System.out.println("5. データベース構造を表示");
050      System.out.println("0. 終了");
051      System.out.print("\n選択してください (0-5): ");
052
053      try {
054        int choice = scanner.nextInt();
055        scanner.nextLine(); // 改行を消費
056
057        switch (choice) {
058          case 1:
059            startWebConsole();
060            break;
061          case 2:
062            showTables();
063            break;
064          case 3:
065            showTableData(scanner);
066            break;
067          case 4:
068            executeCustomQuery(scanner);
069            break;
070          case 5:
071            showDatabaseStructure();
072            break;
073          case 0:
074            System.out.println("終了します。");
075            return;
076          default:
077            System.out.println("無効な選択です。0-5の番号を入力してください。");
078        }
079      } catch (Exception e) {
080        System.out.println("エラー: " + e.getMessage());
081        scanner.nextLine(); // エラー時の入力をクリア
082      }
083    }
084  }
085
086  /** H2 Web Consoleを起動 */
087  private static void startWebConsole() {
088    System.out.println("⚠️  Web Console機能は現在無効になっています");
089    System.out.println("💡 代替案:");
090    System.out.println("   - メニューの「2. テーブル一覧を表示」でテーブルを確認");
091    System.out.println("   - メニューの「3. テーブルのデータを表示」でデータを確認");
092    System.out.println("   - メニューの「4. カスタムSQLクエリを実行」でクエリを実行");
093    System.out.println("   - QuickDatabaseLookupクラスも利用可能です");
094
095    /*
096    // H2 Web Server依存関係の問題により一時的に無効化
097    try {
098      Server webServer = Server.createWebServer("-webAllowOthers", "-webPort", "8082");
099      webServer.start();
100
101      System.out.println("✅ H2 Web Console が起動されました!");
102      System.out.println("📖 ブラウザで以下のURLにアクセスしてください:");
103      System.out.println("   http://localhost:8082");
104      System.out.println("\n💡 接続情報:");
105      System.out.println("   JDBC URL: " + DEFAULT_DB_URL);
106      System.out.println("   User Name: (空白)");
107      System.out.println("   Password: (空白)");
108      System.out.println("\n⚠️  注意: このプロセスを終了するとWebコンソールも停止します");
109
110    } catch (SQLException e) {
111      System.err.println("Web Console の起動に失敗しました: " + e.getMessage());
112    }
113    */
114  }
115
116  /** テーブル一覧を表示 */
117  private static void showTables() {
118    try (Connection conn = DriverManager.getConnection(DEFAULT_DB_URL)) {
119      String query =
120          "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PUBLIC'";
121
122      try (PreparedStatement stmt = conn.prepareStatement(query);
123          ResultSet rs = stmt.executeQuery()) {
124
125        System.out.println("\n📋 テーブル一覧:");
126        while (rs.next()) {
127          System.out.println("  - " + rs.getString("TABLE_NAME"));
128        }
129      }
130    } catch (SQLException e) {
131      System.err.println("テーブル一覧の取得に失敗しました: " + e.getMessage());
132    }
133  }
134
135  /** 指定したテーブルのデータを表示 */
136  private static void showTableData(Scanner scanner) {
137    System.out.print("テーブル名を入力してください: ");
138    String tableName = scanner.nextLine().trim();
139
140    if (tableName.isEmpty()) {
141      System.out.println("テーブル名が入力されていません。");
142      return;
143    }
144
145    try (Connection conn = DriverManager.getConnection(DEFAULT_DB_URL)) {
146      String query = "SELECT * FROM " + tableName;
147
148      try (PreparedStatement stmt = conn.prepareStatement(query);
149          ResultSet rs = stmt.executeQuery()) {
150
151        System.out.println("\n📊 テーブル「" + tableName + "」のデータ:");
152        printResultSet(rs);
153      }
154    } catch (SQLException e) {
155      System.err.println("データの取得に失敗しました: " + e.getMessage());
156    }
157  }
158
159  /** カスタムSQLクエリを実行 */
160  private static void executeCustomQuery(Scanner scanner) {
161    System.out.print("SQLクエリを入力してください: ");
162    String query = scanner.nextLine().trim();
163
164    if (query.isEmpty()) {
165      System.out.println("クエリが入力されていません。");
166      return;
167    }
168
169    try (Connection conn = DriverManager.getConnection(DEFAULT_DB_URL)) {
170      try (PreparedStatement stmt = conn.prepareStatement(query)) {
171
172        if (query.trim().toUpperCase().startsWith("SELECT")) {
173          // SELECT文の場合
174          try (ResultSet rs = stmt.executeQuery()) {
175            System.out.println("\n🔍 クエリ結果:");
176            printResultSet(rs);
177          }
178        } else {
179          // INSERT, UPDATE, DELETE等の場合
180          int affectedRows = stmt.executeUpdate();
181          System.out.println("✅ 実行完了。影響を受けた行数: " + affectedRows);
182        }
183      }
184    } catch (SQLException e) {
185      System.err.println("クエリの実行に失敗しました: " + e.getMessage());
186    }
187  }
188
189  /** データベース構造を表示 */
190  private static void showDatabaseStructure() {
191    try (Connection conn = DriverManager.getConnection(DEFAULT_DB_URL)) {
192      // テーブルとカラム情報を取得
193      String query =
194          """
195                SELECT
196                    TABLE_NAME,
197                    COLUMN_NAME,
198                    DATA_TYPE,
199                    IS_NULLABLE,
200                    COLUMN_DEFAULT
201                FROM INFORMATION_SCHEMA.COLUMNS
202                WHERE TABLE_SCHEMA = 'PUBLIC'
203                ORDER BY TABLE_NAME, ORDINAL_POSITION
204                """;
205
206      try (PreparedStatement stmt = conn.prepareStatement(query);
207          ResultSet rs = stmt.executeQuery()) {
208
209        System.out.println("\n🏗️  データベース構造:");
210
211        String currentTable = "";
212        while (rs.next()) {
213          String tableName = rs.getString("TABLE_NAME");
214
215          if (!tableName.equals(currentTable)) {
216            if (!currentTable.isEmpty()) {
217              System.out.println();
218            }
219            System.out.println("📋 テーブル: " + tableName);
220            currentTable = tableName;
221          }
222
223          String columnName = rs.getString("COLUMN_NAME");
224          String dataType = rs.getString("DATA_TYPE");
225          String nullable = rs.getString("IS_NULLABLE");
226          String defaultValue = rs.getString("COLUMN_DEFAULT");
227
228          System.out.printf(
229              "  ├─ %-15s %-15s %s%s%n",
230              columnName,
231              dataType,
232              "YES".equals(nullable) ? "(NULL可)" : "(NOT NULL)",
233              defaultValue != null ? " デフォルト:" + defaultValue : "");
234        }
235      }
236    } catch (SQLException e) {
237      System.err.println("データベース構造の取得に失敗しました: " + e.getMessage());
238    }
239  }
240
241  /** ResultSetの内容を整形して表示 */
242  private static void printResultSet(ResultSet rs) throws SQLException {
243    ResultSetMetaData metaData = rs.getMetaData();
244    int columnCount = metaData.getColumnCount();
245
246    // ヘッダー行を表示
247    for (int i = 1; i <= columnCount; i++) {
248      System.out.printf("%-20s", metaData.getColumnName(i));
249    }
250    System.out.println();
251
252    // 区切り線
253    for (int i = 1; i <= columnCount; i++) {
254      System.out.print("--------------------");
255    }
256    System.out.println();
257
258    // データ行を表示
259    while (rs.next()) {
260      for (int i = 1; i <= columnCount; i++) {
261        String value = rs.getString(i);
262        System.out.printf("%-20s", value != null ? value : "(NULL)");
263      }
264      System.out.println();
265    }
266  }
267
268  /** デモ用データベースの初期化 */
269  private static void initializeDemoDatabase() throws SQLException {
270    try (Connection conn = DriverManager.getConnection(DEFAULT_DB_URL)) {
271      // テーブルを作成してデータを挿入(既存の場合はスキップ)
272      String checkTable =
273          "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'CUSTOMERS'";
274      try (PreparedStatement stmt = conn.prepareStatement(checkTable);
275          ResultSet rs = stmt.executeQuery()) {
276
277        if (rs.next() && rs.getInt(1) > 0) {
278          System.out.println("✅ デモデータベースは既に初期化済みです");
279          return;
280        }
281      }
282
283      // テーブル作成とデータ挿入(DatabaseRuleDemoと同じ)
284      setupDemoData(conn);
285      System.out.println("✅ デモデータベースを初期化しました");
286    }
287  }
288
289  private static void setupDemoData(Connection conn) throws SQLException {
290    // テーブル作成
291    conn.prepareStatement(
292            """
293            CREATE TABLE customers (
294                id INTEGER PRIMARY KEY,
295                name VARCHAR(100),
296                email VARCHAR(100),
297                phone VARCHAR(20)
298            )
299            """)
300        .execute();
301
302    conn.prepareStatement(
303            """
304            CREATE TABLE products (
305                code VARCHAR(20) PRIMARY KEY,
306                name VARCHAR(100),
307                price DECIMAL(10,2),
308                category VARCHAR(50)
309            )
310            """)
311        .execute();
312
313    conn.prepareStatement(
314            """
315            CREATE TABLE departments (
316                code VARCHAR(10) PRIMARY KEY,
317                name VARCHAR(50),
318                manager VARCHAR(50)
319            )
320            """)
321        .execute();
322
323    // サンプルデータ挿入
324    insertSampleData(conn);
325  }
326
327  private static void insertSampleData(Connection conn) throws SQLException {
328    // 顧客データ
329    PreparedStatement customerStmt =
330        conn.prepareStatement("INSERT INTO customers (id, name, email, phone) VALUES (?, ?, ?, ?)");
331
332    customerStmt.setInt(1, 1001);
333    customerStmt.setString(2, "田中太郎");
334    customerStmt.setString(3, "tanaka@example.com");
335    customerStmt.setString(4, "090-1234-5678");
336    customerStmt.execute();
337
338    customerStmt.setInt(1, 1002);
339    customerStmt.setString(2, "佐藤花子");
340    customerStmt.setString(3, "sato@example.com");
341    customerStmt.setString(4, "090-9876-5432");
342    customerStmt.execute();
343
344    customerStmt.setInt(1, 1003);
345    customerStmt.setString(2, "鈴木一郎");
346    customerStmt.setString(3, "suzuki@example.com");
347    customerStmt.setString(4, "090-5555-7777");
348    customerStmt.execute();
349
350    // 商品データ
351    PreparedStatement productStmt =
352        conn.prepareStatement(
353            "INSERT INTO products (code, name, price, category) VALUES (?, ?, ?, ?)");
354
355    productStmt.setString(1, "LAPTOP001");
356    productStmt.setString(2, "高性能ビジネスノートPC");
357    productStmt.setBigDecimal(3, new java.math.BigDecimal("98000.00"));
358    productStmt.setString(4, "PC");
359    productStmt.execute();
360
361    productStmt.setString(1, "MOUSE001");
362    productStmt.setString(2, "無線光学マウス");
363    productStmt.setBigDecimal(3, new java.math.BigDecimal("2980.00"));
364    productStmt.setString(4, "周辺機器");
365    productStmt.execute();
366
367    // 部署データ
368    PreparedStatement deptStmt =
369        conn.prepareStatement("INSERT INTO departments (code, name, manager) VALUES (?, ?, ?)");
370
371    deptStmt.setString(1, "DEV");
372    deptStmt.setString(2, "開発部");
373    deptStmt.setString(3, "山田部長");
374    deptStmt.execute();
375
376    deptStmt.setString(1, "SALES");
377    deptStmt.setString(2, "営業部");
378    deptStmt.setString(3, "田村部長");
379    deptStmt.execute();
380  }
381}