はじめに
「Railsの『ActiveRecord』やLaravelの『Eloquent』などのORM(O/Rマッピング)に慣れて、あまりSQLを書くことがない」ということはありませんか?
この記事は、SQLの基礎知識を学んだ方、そしてJava初学者を対象に、MyBatisの基本的な使い方を解説します。実践に活かしやすいように必要最低限の情報だけをまとめました。
MyBatisとは
MyBatisは、Javaアプリケーションからデータベースを操作するための永続化フレームワークです。SQLをXMLファイルまたはアノテーションで記述することで、JDBC(Java Database Connectivity)を直接操作する手間を省き、オブジェクトとリレーショナルデータベース間のマッピングを容易にします。
MyBatisの特徴
主な特徴は以下の通りです。
- SQLの分離: JavaコードからSQLを分離し、XMLファイルに記述することで、SQLの管理がしやすくなります
- 柔軟なSQL記述: 動的なSQLを記述できるため、条件に応じた複雑なクエリも簡単に扱えます
- シンプルなマッピング: データベースの行をJavaオブジェクトに自動的にマッピングできます
- 高いパフォーマンス: 必要に応じて手動でSQLを最適化できるため、パフォーマンスを追求しやすいです
ORMとSQLマッピングの違い
ORM (Object-Relational Mapping)
ORMは、データベースのテーブルをプログラミング言語のオブジェクト(クラス)として直接扱うための技術です。開発者はSQLを直接書く代わりに、オブジェクトの操作を通じてデータベースと対話します。
SQLマッピング
開発者がSQLを直接記述し、そのSQLの結果をプログラミング言語のオブジェクトに「マッピング」することを主な役割とする技術です。
ORMとSQLマッピングの比較表
特徴 | ORM(オブジェクトリレーショナルマッピング) | SQLマッピング(SQL Mapper) |
---|---|---|
SQLの扱い | 自動生成される。開発者は原則書かない。 | 開発者が直接記述する。 |
抽象化 | 高い。オブジェクト中心の思考。 | 低い。SQLとデータベース構造を意識する。 |
開発速度 | 定型的なCRUDでは非常に速い。 | 定型的なCRUDではORMより遅い。複雑なクエリでは速い場合がある。 |
パフォーマンス | 自動生成SQLの非効率性からボトルネックになる可能性あり。チューニングはフレームワークの知識が必要。 | SQLを直接書くため、最適化しやすい。開発者のSQLスキルに依存。 |
DBの独立性 | 高い。フレームワークが吸収する。 | 低い。DB固有のSQLを書くと切り替えが難しい。 |
学習コスト | フレームワークの複雑な概念を学ぶ必要がある。 | SQLの知識が必須。マッピングの概念は比較的シンプル。 |
最適なユースケース | オブジェクト指向的な設計を重視し、迅速な開発が必要なシステム。 | 複雑なクエリ、パフォーマンス要求の高い部分、既存DBとの連携が必要なシステム。 |
代表例 | Hibernate/JPA(Java), ActiveRecord(Ruby on Rails), Eloquent ORM(Laravel), SQLAlchemy(Python) | MyBatis(Java), Spring JDBCテンプレート(Java), jOOQ(Java),Dapper(.NET) |
開発環境
この記事では、以下の環境を想定して解説を進めます。
- Java: OpenJDK 17
- Spring Boot: 3.x系 (MyBatis-Spring-Boot-Starterを利用)
- MyBatis: 3.x系
- データベース: postgresql
設定
Spring BootアプリケーションでMyBatisを利用する場合、主にapplication.propertiesまたはapplication.ymlファイルでデータベース接続情報を設定します。
application.properties
spring.datasource.url=jdbc:postgresql://db:5432/dev
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver
mybatis.mapper-locations=classpath:/mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
application.yml
spring:
datasource:
url: jdbc:postgresql://db:5432/dev
username: username
password: password
driver-class-name: org.postgresql.Driver
mybatis:
mapper-locations: classpath:/mapper/*.xml
configuration:
map-underscore-to-camel-case: true
Mapper XMLファイル
MyBatisでは、SQL文をXMLファイルに記述するのが一般的です。これらのファイルをMapper XMLファイルと呼びます。通常、src/main/resources
配下に配置します。
例: UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper">
</mapper>
namespace属性には、通常対応するJavaのMapperインターフェースの完全修飾名を指定します。
sql
sqlタグを使用すると、複数のクエリで共通して使用するSQLフラグメントを定義できます。これにより、SQLの再利用性を高めることができます。
<sql id="userColumns">
id, name, email
</sql>
<select id="selectUserById" resultType="com.example.demo.model.User">
SELECT
<include refid="userColumns"/>
FROM users
WHERE id = #{id}
</select>
resultMap
resultMapは、データベースで取得した結果セットからJavaオブジェクトへのマッピングを詳細に定義するために使用します。特に、テーブルの列名とJavaオブジェクトのプロパティ名が異なる場合や、複合オブジェクト(ネストされたオブジェクトやコレクション)をマッピングする場合に強力です。
<resultMap id="userResultMap" type="com.example.demo.model.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<result property="email" column="user_email"/>
</resultMap>
-
id
: 主キーとなるプロパティと列をマッピングします-
property
: Javaオブジェクトのプロパティ名 -
column
: データベースの列名
-
-
result
: 主キー以外のプロパティと列をマッピングします。idタグと同様にpropertyとcolumn属性を使用します
association
associationは、あるオブジェクトが他の単一のオブジェクトを含む場合(1対1の関係)にマッピングを定義します。
<resultMap id="userAddressResultMap" type="com.example.demo.model.User">
<id property="id" column="user_id"/>
<result property="name" column="user_name"/>
<association property="address" javaType="com.example.demo.model.Address">
<id property="id" column="address_id"/>
<result property="street" column="street_name"/>
<result property="city" column="city_name"/>
</association>
</resultMap>
collection
collectionは、あるオブジェクトが他のオブジェクトのコレクションを含む場合(1対多の関係)にマッピングを定義します。
<resultMap id="blogResultMap" type="com.example.demo.model.Blog">
<id property="id" column="blog_id"/>
<result property="title" column="blog_title"/>
<collection property="posts" ofType="com.example.demo.model.Post">
<id property="id" column="post_id"/>
<result property="title" column="post_title"/>
<result property="content" column="post_content"/>
</collection>
</resultMap>
select
selectタグは、データベースからデータを取得するためのSQLクエリを定義します。
<select id="findAllUsers" resultType="com.example.demo.model.User">
SELECT id, name, email FROM users
</select>
<select id="findUserById" parameterType="int" resultMap="userResultMap">
SELECT user_id, user_name, user_email FROM users WHERE user_id = #{id}
</select>
-
id
: Mapperインターフェースのメソッド名と一致させます -
parameterType
: SQLクエリに渡される引数の型を指定します。Javaのプリミティブ型や完全修飾クラス名を指定できます。省略することも可能です -
resultType
: クエリの結果をマッピングするJavaオブジェクトの完全修飾クラス名を指定します。データベースの列名とJavaオブジェクトのプロパティ名が一致する場合に便利です。 -
resultMap
: resultTypeの代わりにresultMapのIDを指定することで、より詳細なマッピングルールを適用できます
insert, update, delete
insert, update, deleteタグは、それぞれデータベースにデータを挿入、更新、削除するためのSQLクエリを定義します。
<insert id="insertUser" parameterType="com.example.demo.model.User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (name, email) VALUES (#{name}, #{email})
</insert>
<update id="updateUser" parameterType="com.example.demo.model.User">
UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}
</update>
<delete id="deleteUser" parameterType="int">
DELETE FROM users WHERE id = #{id}
</delete>
-
useGeneratedKeys
: データベースが自動生成するキー(例: オートインクリメントのID)をMyBatisが取得し、指定されたkeyPropertyにセットするかどうかを指定します -
keyProperty
: 自動生成されたキーをセットするJavaオブジェクトのプロパティ名を指定します
おわりに
ORM(O/Rマッピング)による基本的なCRUD操作は、SQLを書かずに実装できるのでスピーディーに開発するのに適しています。
対してMyBatisのようなSQLマッピングは、開発者がSQLを直接記述する自由度と引き換えに、データベース操作のより細かい制御とパフォーマンスの最適化が可能です。
システム全体でどちらか一方だけを使うのではなく、両者を組み合わせる「ハイブリッド」なアプローチも大切です。
参考記事
- MyBatis 3 | Documentation
https://mybatis.org/mybatis-3/ja/index.html?hl=ja-JP