0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

jackcess解説

Posted at

【Java】AccessファイルをJavaで操作!Jackcessライブラリ完全ガイド+DAOパターン実装例

はじめに

Microsoft Accessのファイル(.mdb、.accdb)をJavaから直接操作したいと思ったことはありませんか?

「AccessがインストールされていないLinuxサーバーでAccessファイルを読みたい」
「既存システムのデータをAccessファイルにエクスポートしたい」
「AccessファイルをJavaアプリケーションで処理したい」

そんな時に役立つのがJackcessライブラリです!

この記事では、Jackcessの基本的な使い方から、DAOパターンを使った実践的なデータベース移行まで、初心者にもわかりやすく解説します。

対象読者

  • Javaの基礎を学習中の方
  • AccessファイルをJavaで扱いたい方
  • データベース間のデータ移行を実装したい方
  • DAOパターンを実践で学びたい方

環境

  • Java 8以上
  • Jackcess 4.0.5
  • Maven または Gradle

目次

  1. Jackcessとは
  2. セットアップ
  3. 基本操作
  4. 実践的な使い方
  5. DAOパターンでDB連携
  6. トラブルシューティング

Jackcessとは

JackcessはPure Javaで書かれたライブラリで、Microsoft Accessのデータベースファイルを読み書きできます。

🎯 主な特徴

特徴 説明
Accessなしで動作 Microsoft Accessのインストール不要
クロスプラットフォーム Windows、Mac、Linuxで動作
無料 Apache Licenseで提供
幅広いバージョン対応 Access 2000〜2019に対応

セットアップ

Maven

<dependency>
    <groupId>com.healthmarketscience.jackcess</groupId>
    <artifactId>jackcess</artifactId>
    <version>4.0.5</version>
</dependency>

Gradle

dependencies {
    implementation 'com.healthmarketscience.jackcess:jackcess:4.0.5'
}

基本操作

1️⃣ データベースを開く

import com.healthmarketscience.jackcess.*;
import java.io.File;

// try-with-resourcesで自動的にクローズ
try (Database db = DatabaseBuilder.open(new File("sample.mdb"))) {
    System.out.println("データベースを開きました");
    // 処理
} // ここで自動的にクローズされる

2️⃣ データを読み取る

try (Database db = DatabaseBuilder.open(new File("sample.mdb"))) {
    Table table = db.getTable("顧客テーブル");
    
    // 拡張for文で簡単にループ
    for (Row row : table) {
        String name = row.getString("名前");
        Integer age = row.getInt("年齢");
        System.out.println(String.format("%s さん(%d歳)", name, age));
    }
}

3️⃣ データを検索する

try (Database db = DatabaseBuilder.open(new File("sample.mdb"))) {
    Table table = db.getTable("顧客テーブル");
    
    // 特定の条件でデータを検索
    Map<String, Object> criteria = Collections.singletonMap("名前", "田中太郎");
    Row row = CursorBuilder.findRow(table, criteria);
    
    if (row != null) {
        System.out.println("見つかりました: " + row);
    }
}

4️⃣ データを追加する

try (Database db = DatabaseBuilder.open(new File("sample.mdb"))) {
    Table table = db.getTable("顧客テーブル");
    
    // addRowメソッドで簡単に追加
    table.addRow(
        Column.AUTO_NUMBER,  // ID(自動採番)
        "山田花子",          // 名前
        25,                  // 年齢
        "東京都"             // 住所
    );
}

5️⃣ 新しいデータベースを作成

// 新規Accessファイルを作成
try (Database db = DatabaseBuilder.create(
    Database.FileFormat.V2010,  // Access 2010形式
    new File("new_database.accdb"))) {
    
    // テーブルを作成
    Table newTable = new TableBuilder("商品マスタ")
        .addColumn(new ColumnBuilder("商品ID", DataType.LONG)
            .setAutoNumber(true))
        .addColumn(new ColumnBuilder("商品名", DataType.TEXT))
        .addColumn(new ColumnBuilder("価格", DataType.MONEY))
        .toTable(db);
    
    // データを追加
    newTable.addRow(Column.AUTO_NUMBER, "ノートPC", 98000);
    newTable.addRow(Column.AUTO_NUMBER, "マウス", 2980);
}

実践的な使い方

CSVファイルをインポート

try (Database db = DatabaseBuilder.open(new File("database.mdb"))) {
    // CSVファイルを直接インポート
    new ImportUtil.Builder(db, "CSVデータ")
        .setDelimiter(",")
        .setHeader(true)
        .importFile(new File("data.csv"));
    
    System.out.println("CSVインポート完了!");
}

データ型の対応表

Jackcess型 Java型 用途
TEXT String 文字列
INT Integer 整数
LONG Long ID等の長整数
MONEY BigDecimal 金額
BOOLEAN Boolean フラグ
SHORT_DATE_TIME LocalDateTime 日時

DAOパターンでDB連携

実際の開発では、DAOパターンを使ってデータベースアクセスを抽象化することが重要です。
ここでは、MySQLなどの既存DBからAccessファイルへデータを移行する実装例を紹介します。

📁 プロジェクト構造

src/main/java/
├── entity/
│   └── Employee.java       # エンティティクラス
├── dao/
│   ├── EmployeeDAO.java    # DAOインターフェース
│   ├── MySQLEmployeeDAO.java    # MySQL実装
│   └── AccessEmployeeDAO.java   # Access実装
└── DatabaseMigration.java  # メインクラス

Step 1: エンティティクラス

// entity/Employee.java
public class Employee {
    private Integer id;
    private String name;
    private String department;
    private Integer age;
    private BigDecimal salary;
    private LocalDateTime hireDate;
    
    // コンストラクタ
    public Employee() {}
    
    public Employee(Integer id, String name, String department, 
                   Integer age, BigDecimal salary, LocalDateTime hireDate) {
        this.id = id;
        this.name = name;
        this.department = department;
        this.age = age;
        this.salary = salary;
        this.hireDate = hireDate;
    }
    
    // Getter/Setter(Lombokを使えば @Data で省略可能)
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    // 他のGetter/Setterも同様に実装...
}

Step 2: DAOインターフェース

// dao/EmployeeDAO.java
public interface EmployeeDAO {
    List<Employee> findAll();                    // 全件取得
    Employee findById(Integer id);               // ID検索
    List<Employee> findByDepartment(String dept); // 部署検索
    void save(Employee employee);                // 保存
    void saveAll(List<Employee> employees);      // 一括保存
    void close();                                // リソース解放
}

Step 3: MySQL実装

// dao/MySQLEmployeeDAO.java
public class MySQLEmployeeDAO implements EmployeeDAO {
    private Connection connection;
    
    public MySQLEmployeeDAO() throws SQLException {
        // データベース接続
        String url = "jdbc:mysql://localhost:3306/company_db?useUnicode=true&characterEncoding=utf8";
        String user = "root";
        String password = "password";
        this.connection = DriverManager.getConnection(url, user, password);
    }
    
    @Override
    public List<Employee> findAll() {
        List<Employee> employees = new ArrayList<>();
        String sql = "SELECT * FROM employees ORDER BY id";
        
        try (Statement stmt = connection.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {
            
            while (rs.next()) {
                employees.add(mapRowToEmployee(rs));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        
        return employees;
    }
    
    // ResultSetからEmployeeへのマッピング
    private Employee mapRowToEmployee(ResultSet rs) throws SQLException {
        Employee emp = new Employee();
        emp.setId(rs.getInt("id"));
        emp.setName(rs.getString("name"));
        emp.setDepartment(rs.getString("department"));
        emp.setAge(rs.getInt("age"));
        emp.setSalary(rs.getBigDecimal("salary"));
        
        Timestamp timestamp = rs.getTimestamp("hire_date");
        if (timestamp != null) {
            emp.setHireDate(timestamp.toLocalDateTime());
        }
        
        return emp;
    }
    
    @Override
    public void close() {
        try {
            if (connection != null && !connection.isClosed()) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
    // 他のメソッドも実装...
}

Step 4: Access実装

// dao/AccessEmployeeDAO.java
public class AccessEmployeeDAO implements EmployeeDAO {
    private Database database;
    private Table employeeTable;
    
    public AccessEmployeeDAO(String fileName) throws IOException {
        File dbFile = new File(fileName);
        
        // ファイルが存在しない場合は新規作成
        if (!dbFile.exists()) {
            createNewDatabase(dbFile);
        } else {
            this.database = DatabaseBuilder.open(dbFile);
            this.employeeTable = database.getTable("社員マスタ");
        }
    }
    
    private void createNewDatabase(File dbFile) throws IOException {
        // 新規データベース作成
        this.database = DatabaseBuilder.create(
            Database.FileFormat.V2010, dbFile);
        
        // テーブル作成
        this.employeeTable = new TableBuilder("社員マスタ")
            .addColumn(new ColumnBuilder("社員ID", DataType.LONG)
                .setAutoNumber(true))
            .addColumn(new ColumnBuilder("氏名", DataType.TEXT)
                .setMaxLength(100))
            .addColumn(new ColumnBuilder("部署", DataType.TEXT)
                .setMaxLength(50))
            .addColumn(new ColumnBuilder("年齢", DataType.INT))
            .addColumn(new ColumnBuilder("給与", DataType.MONEY))
            .addColumn(new ColumnBuilder("入社日", DataType.SHORT_DATE_TIME))
            .addIndex(new IndexBuilder("idx_部署")
                .addColumns("部署"))
            .toTable(database);
    }
    
    @Override
    public void saveAll(List<Employee> employees) {
        int count = 0;
        for (Employee emp : employees) {
            try {
                employeeTable.addRow(
                    Column.AUTO_NUMBER,
                    emp.getName(),
                    emp.getDepartment(),
                    emp.getAge(),
                    emp.getSalary(),
                    emp.getHireDate()
                );
                count++;
                
                // 進捗表示(100件ごと)
                if (count % 100 == 0) {
                    System.out.println(count + "件処理済み...");
                }
            } catch (IOException e) {
                System.err.println("保存エラー: " + emp.getName());
                e.printStackTrace();
            }
        }
        System.out.println("合計 " + count + " 件を保存しました");
    }
    
    @Override
    public void close() {
        try {
            if (database != null) {
                database.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 他のメソッドも実装...
}

Step 5: 実行クラス

// DatabaseMigration.java
public class DatabaseMigration {
    
    public static void main(String[] args) {
        System.out.println("=== データベース移行開始 ===");
        
        // try-with-resourcesを活用
        try (MySQLEmployeeDAO mysqlDAO = new MySQLEmployeeDAO();
             AccessEmployeeDAO accessDAO = new AccessEmployeeDAO("社員データ.accdb")) {
            
            // 1. MySQLからデータ取得
            System.out.println("データ取得中...");
            List<Employee> employees = mysqlDAO.findAll();
            System.out.println("取得件数: " + employees.size());
            
            // 2. Accessへ保存
            System.out.println("Access保存中...");
            accessDAO.saveAll(employees);
            
            // 3. 部署別ファイル作成(オプション)
            createDepartmentFiles(mysqlDAO);
            
            System.out.println("=== 移行完了 ===");
            
        } catch (Exception e) {
            System.err.println("エラー発生: " + e.getMessage());
            e.printStackTrace();
        }
    }
    
    // 部署別ファイル作成
    private static void createDepartmentFiles(MySQLEmployeeDAO dao) {
        String[] departments = {"営業部", "開発部", "総務部"};
        
        for (String dept : departments) {
            try (AccessEmployeeDAO deptDAO = 
                    new AccessEmployeeDAO(dept + "_社員.accdb")) {
                
                List<Employee> deptEmployees = dao.findByDepartment(dept);
                if (!deptEmployees.isEmpty()) {
                    deptDAO.saveAll(deptEmployees);
                    System.out.println(dept + ": " + deptEmployees.size() + "件");
                }
                
            } catch (IOException e) {
                System.err.println(dept + " の処理失敗: " + e.getMessage());
            }
        }
    }
}

応用例:定期バックアップ

@Component  // Spring Bootの場合
public class ScheduledBackup {
    
    @Scheduled(cron = "0 0 * * * *")  // 毎時0分に実行
    public void backupToAccess() {
        String timestamp = LocalDateTime.now()
            .format(DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"));
        String fileName = String.format("backup_%s.accdb", timestamp);
        
        try (MySQLEmployeeDAO source = new MySQLEmployeeDAO();
             AccessEmployeeDAO backup = new AccessEmployeeDAO(fileName)) {
            
            List<Employee> data = source.findAll();
            backup.saveAll(data);
            
            System.out.println("バックアップ完了: " + fileName);
            
        } catch (Exception e) {
            System.err.println("バックアップ失敗: " + e.getMessage());
        }
    }
}

トラブルシューティング

💡 よくあるエラーと対処法

1. ファイルが見つからない

File dbFile = new File("data.mdb");
if (!dbFile.exists()) {
    System.err.println("ファイルが存在しません: " + dbFile.getAbsolutePath());
    return;
}

2. 文字化け対策

// JVMオプションで指定
-Dfile.encoding=UTF-8

3. メモリ不足(大量データ処理時)

// バッチ処理で分割
int batchSize = 1000;
for (int i = 0; i < employees.size(); i += batchSize) {
    List<Employee> batch = employees.subList(i, 
        Math.min(i + batchSize, employees.size()));
    accessDAO.saveAll(batch);
}

必要な依存関係まとめ

<dependencies>
    <!-- Jackcess -->
    <dependency>
        <groupId>com.healthmarketscience.jackcess</groupId>
        <artifactId>jackcess</artifactId>
        <version>4.0.5</version>
    </dependency>
    
    <!-- MySQL Driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    
    <!-- ログ出力(オプション) -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>2.0.9</version>
    </dependency>
</dependencies>

パフォーマンスTips

🚀 高速化のコツ

  1. インデックスを活用
.addIndex(new IndexBuilder("idx_name").addColumns("name"))
  1. バッチ処理
// 1000件ずつ処理
List<List<Employee>> batches = Lists.partition(employees, 1000);
  1. 接続プーリング(HikariCP使用例)
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/db");
HikariDataSource dataSource = new HikariDataSource(config);

まとめ

Jackcessを使えば、JavaからAccessファイルを簡単に操作できます。

基本操作:読み取り、書き込み、検索、作成
DAOパターン:データベース操作の抽象化
実践例:MySQLからAccessへのデータ移行
応用:定期バックアップ、バッチ処理

この記事のコードを参考に、ぜひ実際に動かしてみてください!

参考リンク

関連記事

もしJavaでExcelファイルを操作したい場合は、Apache POIライブラリがおすすめです。
CSVファイルの操作なら、OpenCSVやApache Commons CSVが便利です。


タグ候補: Java Jackcess MicrosoftAccess DAO データベース

ご質問やご意見があればコメントください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?