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に習熟している開発者が必要になる