LoginSignup
2

posted at

updated at

Spring O/R Mapperの使い分け

O/R Mapperとは何か

O/R Mapperはオブジェクト指向言語で書かれたアプリケーションと非オブジェクト指向であるデータベースやSQLの間のインピーダンスミスマッチを解消させるためのフレームワークである。今回はメジャーなO/RMapperである以下のフレームワークについて調査する。

  • JPA(Java Persistance API)
  • MyBatis

JPA(Java Persistance API)

  • Domainに@Entity等のアノテーションを付与することで設定する
  • CRUDに関わる基本的なメソッドがEntityManagerから提供されるため、冗長なSQLの記載が不要になる
  • 独自のクエリを発行する際は、SQLライクな独自のクエリ言語であるJPQL(Java Persistance Query Language)を使用する

以下にDomainの実装例を示す。

package com.example.todo.domain.model;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name = "todo")
public class Todo implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "todo_id")
    private String todoId;
    @Column(name = "todo_title")
    private String todoTitle;
    @Column(name = "finished")
    private boolean finished;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "created_at")
    private Date createdAt;
    public String getTodoId() {
        return todoId;
    }
    public void setTodoId(String todoId) {
        this.todoId = todoId;
    }
    public String getTodoTitle() {
        return todoTitle;
    }
    public void setTodoTitle(String todoTitle) {
        this.todoTitle = todoTitle;
    }
    public boolean isFinished() {
        return finished;
    }
    public void setFinished(boolean finished) {
        this.finished = finished;
    }
    public Date getCreatedAt() {
        return createdAt;
    }
    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }
}

以下にRepositoryの実装例を示す。 なお、RepositoryImplは自動生成されるため作成不要である。

package com.example.todo.domain.repository.todo;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import com.example.todo.domain.model.Todo;

public interface TodoRepository extends JpaRepository<Todo, String> {

    @Query("SELECT COUNT(t) FROM Todo t WHERE t.finished = :finished")
    long countByFinished(@Param("finished") boolean finished);

}

MyBatis

  • O/R MapperというよりもSQLとオブジェクトをマッピングするSQL Mapperという表現が正しい
  • Mapperファイル(xml)にSQLとオブジェクトのメソッドとのマッピングを定義する
  • SQLでデータベースの操作ができつつも、ビジネスロジックからSQL自体を隠蔽できる

以下にMapperファイルの例を示す。 なお、Repositoryについては通常と同様にエンティティとプロパティを定義する。

<?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="com.example.todo.domain.repository.todo.TodoRepository">

    <resultMap id="todoResultMap" type="Todo">
        <id property="todoId" column="todo_id" />
        <result property="todoTitle" column="todo_title" />
        <result property="finished" column="finished" />
        <result property="createdAt" column="created_at" />
    </resultMap>

    <select id="findById" parameterType="String" resultMap="todoResultMap">
    <![CDATA[
        SELECT
            todo_id,
            todo_title,
            finished,
            created_at
        FROM
            todo
        WHERE
            todo_id = #{todoId}
    ]]>
    </select>

    <select id="findAll" resultMap="todoResultMap">
    <![CDATA[
        SELECT
            todo_id,
            todo_title,
            finished,
            created_at
        FROM
            todo
    ]]>
    </select>

    <insert id="create" parameterType="Todo">
    <![CDATA[
        INSERT INTO todo
        (
            todo_id,
            todo_title,
            finished,
            created_at
        )
        VALUES
        (
            #{todoId},
            #{todoTitle},
            #{finished},
            #{createdAt}
        )
    ]]>
    </insert>

    <update id="update" parameterType="Todo">
    <![CDATA[
        UPDATE todo
        SET
            todo_title = #{todoTitle},
            finished = #{finished},
            created_at = #{createdAt}
        WHERE
            todo_id = #{todoId}
    ]]>
    </update>

    <delete id="delete" parameterType="Todo">
    <![CDATA[
        DELETE FROM
            todo
        WHERE
            todo_id = #{todoId}
    ]]>
    </delete>

    <select id="countByFinished" parameterType="Boolean"
        resultType="Long">
    <![CDATA[
        SELECT
            COUNT(*)
        FROM
            todo
        WHERE
            finished = #{finished}
    ]]>
    </select>

</mapper>

MyBatisとJPAの使い分け

  • 複雑なクエリが必要であり、今後もカスタマイズが想定される場合はカスタマイズ性の高いMyBatisを使用する
  • 単純なクエリしか必要ない、即座にモックアップ的なアプリケーションを構築する場合はJPAを使用する
  • JPAの場合、JPQLという独自のクエリ言語を用いるため、JPAに習熟している開発者が必要になる

参考文献

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
What you can do with signing up
2