Java アプリケーション2

差分比較が遅い場合、いくつかの方法でパフォーマンスを向上させることができます。特に、以下の方法を試してみることをお勧めします：

	1.	インデックスの作成：
テーブルにインデックスを作成することで、検索と比較のパフォーマンスを大幅に向上させることができます。
	2.	適切なデータ型の使用：
テーブルのカラムに適切なデータ型を使用することで、パフォーマンスを向上させることができます。
	3.	一時テーブルの使用：
一時テーブルを使用して、データの読み込みや比較を効率的に行うことができます。

以下のコード例では、上記の改善を適用しています。

改善したコード例
```Java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.CSVWriter;
import com.opencsv.exceptions.CsvValidationException;

public class CsvToSQLiteApp extends JFrame {
    private static final String DB_URL = "jdbc:sqlite:example.db";
    private JTextField beforeDirPathField;
    private JTextField afterDirPathField;
    private JTextField outputCsvPathField;
    private JCheckBox headerCheckBox;
    private JLabel statusLabel;

    public CsvToSQLiteApp() {
        setTitle("CSV to SQLite App");
        setSize(700, 400);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setLayout(new GridBagLayout());

        GridBagConstraints gbc = new GridBagConstraints();
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.insets = new Insets(5, 5, 5, 5);

        JLabel beforeDirPathLabel = new JLabel("比較元ディレクトリパス:");
        beforeDirPathField = new JTextField(20);
        JButton selectBeforeDirButton = new JButton("Select Before Directory");

        JLabel afterDirPathLabel = new JLabel("比較先ディレクトリパス:");
        afterDirPathField = new JTextField(20);
        JButton selectAfterDirButton = new JButton("Select After Directory");

        JLabel outputCsvPathLabel = new JLabel("Output CSV Path:");
        outputCsvPathField = new JTextField(20);
        JButton diffButton = new JButton("Export Difference to CSV");

        headerCheckBox = new JCheckBox("ヘッダー行あり");
        headerCheckBox.setSelected(true);

        statusLabel = new JLabel();
        statusLabel.setForeground(Color.BLUE);

        gbc.gridx = 0;
        gbc.gridy = 0;
        add(beforeDirPathLabel, gbc);

        gbc.gridx = 1;
        add(beforeDirPathField, gbc);

        gbc.gridx = 2;
        add(selectBeforeDirButton, gbc);

        gbc.gridx = 0;
        gbc.gridy = 1;
        add(afterDirPathLabel, gbc);

        gbc.gridx = 1;
        add(afterDirPathField, gbc);

        gbc.gridx = 2;
        add(selectAfterDirButton, gbc);

        gbc.gridx = 0;
        gbc.gridy = 2;
        add(outputCsvPathLabel, gbc);

        gbc.gridx = 1;
        add(outputCsvPathField, gbc);

        gbc.gridx = 2;
        add(diffButton, gbc);

        gbc.gridx = 0;
        gbc.gridy = 3;
        add(headerCheckBox, gbc);

        gbc.gridx = 0;
        gbc.gridy = 4;
        gbc.gridwidth = 3;
        add(statusLabel, gbc);

        selectBeforeDirButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                int result = fileChooser.showOpenDialog(null);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File selectedDir = fileChooser.getSelectedFile();
                    beforeDirPathField.setText(selectedDir.getAbsolutePath());
                }
            }
        });

        selectAfterDirButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser fileChooser = new JFileChooser();
                fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                int result = fileChooser.showOpenDialog(null);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File selectedDir = fileChooser.getSelectedFile();
                    afterDirPathField.setText(selectedDir.getAbsolutePath());
                }
            }
        });

        diffButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String beforeDirPath = beforeDirPathField.getText();
                String afterDirPath = afterDirPathField.getText();
                String outputCsvPath = outputCsvPathField.getText();
                boolean hasHeader = headerCheckBox.isSelected();
                try (Connection conn = DriverManager.getConnection(DB_URL)) {
                    importCsvToTable(conn, beforeDirPath, "before", hasHeader);
                    importCsvToTable(conn, afterDirPath, "after", hasHeader);
                    createIndexes(conn, "before");
                    createIndexes(conn, "after");
                    exportDiffToCsv(conn, "before", "after", outputCsvPath);
                    statusLabel.setText("Difference exported to " + outputCsvPath);
                } catch (SQLException ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(null, "Error processing CSV files: " + ex.getMessage());
                }
            }
        });
    }

    private void importCsvToTable(Connection conn, String csvDirPath, String tableName, boolean hasHeader) {
        File folder = new File(csvDirPath);
        File[] listOfFiles = folder.listFiles((dir, name) -> name.endsWith(".csv"));

        if (listOfFiles == null) {
            JOptionPane.showMessageDialog(null, "No CSV files found in the directory: " + csvDirPath);
            return;
        }

        try (Statement stmt = conn.createStatement()) {
            stmt.execute("DROP TABLE IF EXISTS " + tableName);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        for (File file : listOfFiles) {
            try (CSVReader csvReader = new CSVReaderBuilder(new InputStreamReader(new FileInputStream(file), "SJIS"))
                    .withSkipLines(0)
                    .build()) {
                String[] headers = hasHeader ? csvReader.readNext() : generateDefaultHeaders(csvReader.peek());
                if (headers == null) {
                    continue;
                }

                while (headers[0].startsWith("#")) {
                    headers = csvReader.readNext();
                    if (headers == null) {
                        break;
                    }
                }

                createTable(conn, tableName, headers);

                String[] row;
                while ((row = csvReader.readNext()) != null) {
                    if (row[0].startsWith("#")) {
                        continue;
                    }
                    insertRow(conn, tableName, headers, row);
                }
            } catch (IOException | CsvValidationException | SQLException e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(null, "Error processing file " + file.getName() + ": " + e.getMessage());
            }
        }
    }

    private String[] generateDefaultHeaders(String[] sampleRow) {
        String[] headers = new String[sampleRow.length];
        for (int i = 0; i < sampleRow.length; i++) {
            headers[i] = "Column" + (i + 1);
        }
        return headers;
    }

    private void createTable(Connection conn, String tableName, String[] headers) throws SQLException {
        try (Statement stmt = conn.createStatement()) {
            String columns = String.join(" TEXT, ", headers) + " TEXT";
            String createTableSQL = String.format("CREATE TABLE %s (%s)", tableName, columns);
            stmt.execute(createTableSQL);
        }
    }

    private void insertRow(Connection conn, String tableName, String[] headers, String[] row) throws SQLException {
        String columns = String.join(", ", headers);
        String placeholders = String.join(", ", Arrays.asList(row).stream().map(col -> "?").toArray(String[]::new));

        String insertSQL = String.format("INSERT INTO %s (%s) VALUES (%s)", tableName, columns, placeholders);
        try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) {
            for (int i = 0; i < row.length; i++) {
                pstmt.setString(i + 1, row[i]);
            }
            pstmt.executeUpdate();
        }
    }

    private void createIndexes(Connection conn, String tableName) throws SQLException {
        try (Statement stmt = conn.createStatement()) {
            String createIndexSQL = String.format("CREATE INDEX idx_%s ON %s (%s)", tableName, tableName, "column1");
            stmt.execute(createIndexSQL);
        }
    }

    private void exportDiffToCsv(Connection conn, String tableA, String tableB, String outputCsvPath) {
        String sql = String.format("SELECT * FROM %s EXCEPT SELECT * FROM %s", tableA, tableB);

        try (CSVWriter writer = new CSVWriter(new OutputStreamWriter(new FileWriter(outputCsvPath), "SJIS"));
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            writer.writeAll(rs, true);
        } catch (SQLException | IOException e) {
            e.printStackTrace();
            JOptionPane.showMessageDialog(null, "Error exporting difference: " + e.getMessage());
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new CsvToSQLiteApp().setVisible(true));
    }
}
```

### 説明

1. **インデックスの作成**：
   - `createIndexes`メソッドを追加し、指定されたテーブルにインデックスを作成します。この例では、`column1`にインデックスを作成していますが、適切なカラムを指定してください。

2. **テーブルの再作成**：
   - テーブルのデータを再インポートする際に、既存のテーブルを削除して再作成します。これにより、前回のインポートデータをクリアし、最新のデータのみを使用できます。

3. **PreparedStatementの使用**：
   - データ挿入の際に`PreparedStatement`を使用して、パフォーマンスを向上させます。

4. **ヘッダー行の有無の管理**：
   - ヘッダー行の有無を管理するために、`hasHeader`パラメータを使用します。ヘッダーがない場合、`generateDefaultHeaders`メソッドを使用してデフォルトのヘッダーを生成します。

これにより、データのインポートと比較が効率的になり、パフォーマンスが向上します。適切なカラムにインデックスを作成することで、検索と比較のパフォーマンスが大幅に改善されます。