MyBatisにおけるXMLベースの単一テーブルCRUD実装

1. プロジェクト依存関係の構築

MyBatisを使用する場合、まずビルドツールに対してコアライブラリと対象とするRDBMSのドライバーを登録します。以下はMavenプロジェクトでの標準的な設定例です。

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>jp.tech.sample</groupId>
  <artifactId>MyBatisXmlDemo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.13</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.33</version>
    </dependency>
  </dependencies>
</project>

2. ドメインモデルとスキーマ準備

オブジェクトとテーブルの対応付けを行うため、単純なPOJOクラスを定義します。プロパティ命名はキャメルケースを基調とし、デフォルトコンストラクタとアクセサを提供します。

package jp.tech.sample.model;

public class Member {
    private long memberId;
    private String displayName;
    private int memberStatus;

    public Member() {}

    public long getMemberId() { return memberId; }
    public void setMemberId(long memberId) { this.memberId = memberId; }
    public String getDisplayName() { return displayName; }
    public void setDisplayName(String displayName) { this.displayName = displayName; }
    public int getMemberStatus() { return memberStatus; }
    public void setMemberStatus(int memberStatus) { this.memberStatus = memberStatus; }

    @Override
    public String toString() {
        return String.format("Member{Id=%d, Name='%s', Status=%d}", memberId, displayName, memberStatus);
    }
}

対応するデータベース側のDDLは以下の通りです。

CREATE DATABASE IF NOT EXISTS app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE app_db;

CREATE TABLE members (
    member_id BIGINT AUTO_INCREMENT PRIMARY KEY,
    display_name VARCHAR(100) NOT NULL,
    member_status INT DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

3. SQLマッピング定義

SQL文をXMLファイルに分離することで、Javaコードとの結合度を下げます。namespaceにはパッケージ構造を反映させ、各ステートメントに一意の識別子を割り当てます。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="jp.tech.sample.mapping.MemberMapper">

  <!-- 特定IDによる単一検索 -->
  <select id="findMember" parameterType="long" resultType="jp.tech.sample.model.Member">
    SELECT member_id, display_name, member_status FROM members WHERE member_id = #{targetId}
  </select>

  <!-- 全件取得 -->
  <select id="listAll" resultType="jp.tech.sample.model.Member">
    SELECT member_id, display_name, member_status FROM members
  </select>

  <!-- 新規登録(JDBCネイティブの主キー取得を有効化) -->
  <insert id="registerMember" parameterType="jp.tech.sample.model.Member" useGeneratedKeys="true" keyProperty="memberId">
    INSERT INTO members(display_name, member_status) VALUES(#{displayName}, #{memberStatus})
  </insert>

  <!-- 状態更新 -->
  <update id="changeStatus" parameterType="jp.tech.sample.model.Member">
    UPDATE members SET display_name = #{displayName}, member_status = #{memberStatus} WHERE member_id = #{memberId}
  </update>

  <!-- 指定IDによる削除 -->
  <delete id="dropMember" parameterType="long">
    DELETE FROM members WHERE member_id = #{targetId}
  </delete>

</mapper>

4. フレームワーク初期化設定

データソース、トランザクションポリシー、およびMapperファイルの配置場所を定義する構成ファイルを作成します。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="local-env">
    <environment id="local-env">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/app_db?serverTimezone=UTC"/>
        <property name="username" value="db_admin"/>
        <property name="password" value="secure_pass_123"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="jp/tech/sample/mapping/MemberMapper.xml"/>
  </mappers>
</configuration>

5. セッション制御とCRUD実行ロジック

`SqlSessionFactory`から`SqlSession`を取得する際、引数に`true`を設定するとトランザクション自動コミットが有効になります。この場合、明示的な`commit()`呼出は不要となり、コードが簡素化されます。ただし、バッチ処理やロールバック要件がある場合は`false`として手動管理を推奨します。

また、レコード登録後の主キー値取得については、上記マッパーのように`useGeneratedKeys="true"`属性をインサートタグに直接付与する方法が、MySQLやPostgreSQLにおいて最も効率的で標準的な実装パターンとなります。

以下は、作成したセッションを使用して登録・検索・更新・削除を一連のフローで実行する統合サンプルです。

package jp.tech.sample;

import java.io.IOException;
import java.io.Reader;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import jp.tech.sample.model.Member;

public class DataOperationRunner {
    public static void main(String[] args) throws IOException {
        Reader configStream = Resources.getResourceAsReader("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(configStream);
        
        // autoCommit=true を適用し、明示的なコミット省略を実現
        try (SqlSession session = factory.openSession(true)) {
            String baseNs = "jp.tech.sample.mapping.MemberMapper";

            System.out.println(">>> 新規レコード登録");
            Member record = new Member();
            record.setDisplayName("sample_account_01");
            record.setMemberStatus(1);
            
            session.insert(baseNs + ".registerMember", record);
            System.out.println("Assigned Primary Key: " + record.getMemberId());

            System.out.println(">>> 単一レコード取得");
            Member retrieved = session.selectOne(baseNs + ".findMember", record.getMemberId());
            System.out.println(retrieved);

            System.out.println(">>> フィールド更新");
            record.setDisplayName("updated_sample_01");
            session.update(baseNs + ".changeStatus", record);
            
            Member confirmed = session.selectOne(baseNs + ".findMember", record.getMemberId());
            System.out.println("Post-update state: " + confirmed);

            System.out.println(">>> 全件一覧表示");
            List<Member> allRecords = session.selectList(baseNs + ".listAll");
            allRecords.forEach(System.out::println);

            System.out.println(">>> 指定IDによる削除処理");
            session.delete(baseNs + ".dropMember", record.getMemberId());
            
            List<Member> residual = session.selectList(baseNs + ".listAll");
            System.out.println("Remaining records count: " + residual.size());
        }
    }
}

タグ: MyBatis XMLマッピング トランザクション制御 主キー自動採番 JDBC接続プール

6月27日 00:44 投稿