swh:1:snp:113c758bbca8ab355325fa13c5762925d199e835
Tip revision: 669d5b245cbe2efedbf2ae777ed633accf98e9c4 authored by Iwao AVE! on 03 July 2024, 03:06:24 UTC
Reverted to 3.5.16
Reverted to 3.5.16
Tip revision: 669d5b2
dynamic-sql.html
<!DOCTYPE html>
<!--
| Generated by Apache Maven Doxia Site Renderer 2.0.0-M16 from src/site/ja/xdoc/dynamic-sql.xml at 03 7月 2024
| Rendered using Apache Maven Fluido Skin 2.0.0-M8
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="generator" content="Apache Maven Doxia Site Renderer 2.0.0-M16" />
<meta name="author" content="Clinton Begin" />
<meta name="author" content="Iwao AVE!" />
<title>mybatis – MyBatis 3 | 動的 SQL</title>
<link rel="stylesheet" href="./css/apache-maven-fluido-2.0.0-M8.min.css" />
<link rel="stylesheet" href="./css/site.css" />
<link rel="stylesheet" href="./css/print.css" media="print" />
<script src="./js/apache-maven-fluido-2.0.0-M8.min.js"></script>
</head>
<body>
<div class="container-fluid container-fluid-top">
<header>
<div id="banner">
<div class="pull-left"></div>
<div class="pull-right"><div id="bannerRight"><h1><a href="https://blog.mybatis.org/" class="externalLink"><img class="imageLink" src="../../images/mybatis-logo.png" alt="MyBatis logo" /> MyBatis</a></h1></div></div>
<div class="clear"><hr/></div>
</div>
<div id="breadcrumbs">
<ul class="breadcrumb">
<li id="publishDate">最終更新: 02 4月 2024<span class="divider">|</span>
</li>
<li id="projectVersion">バージョン: 3.5.16</li>
</ul>
</div>
</header>
<div class="row-fluid">
<header id="leftColumn" class="span2">
<nav class="well sidebar-nav">
<ul class="nav nav-list">
<li class="nav-header">Core</li>
<li><a href="index.html">イントロダクション</a></li>
<li><a href="getting-started.html">スタートガイド</a></li>
<li><a href="configuration.html"><span class="icon-chevron-right"></span>設定</a></li>
<li><a href="sqlmap-xml.html"><span class="icon-chevron-right"></span>Mapper XML ファイル</a></li>
<li class="active"><a>動的 SQL</a></li>
<li><a href="java-api.html"><span class="icon-chevron-right"></span>Java API</a></li>
<li><a href="statement-builders.html"><span class="icon-chevron-right"></span>ステートメントビルダー</a></li>
<li><a href="logging.html">ロギング</a></li>
<li class="nav-header">プロジェクト文書</li>
<li><a href="project-info.html"><span class="icon-chevron-right"></span>プロジェクト情報</a></li>
<li><a href="project-reports.html"><span class="icon-chevron-right"></span>プロジェクトレポート</a></li>
</ul>
</nav>
<div class="well sidebar-nav">
<div id="poweredBy">
<div class="clear"></div>
<div class="clear"></div>
<div class="clear"></div>
<a href="https://maven.apache.org/" class="builtBy" target="_blank"><img class="builtBy" alt="Built by Maven" src="./images/logos/maven-feather.png" /></a>
</div>
</div>
</header>
<main id="bodyColumn" class="span10">
<section>
<h1>動的 SQL</h1>
<p>Mybatis の強力な機能のひとつに、動的 SQL があります。もし、JDBC や類似のフレームワークを使ったことがあるなら、条件に合うように文字列をつなぎ合わせて、スペースを忘れたり、列のリストの末尾のカンマを削除するのを忘れないように注意しながら SQL を構築するのが如何に大変か分かると思います。動的に SQL を構築するのは大変な苦痛を伴う場合があります。
</p>
<p>動的 SQL の構築が楽しくなることはないでしょうが、MyBatis が提供する強力な動的 SQL 言語を使えばかなり改善することができます。
</p>
<p>JSTL などの XML ベースのテキストプロセッサを使ったことがあるなら、MyBatis の動的 SQL の要素は馴染みやすいものだと思います。以前のバージョンの MyBatis では理解しておかなくてはならない要素が数多くありましたが、MyBatis 3 では改良の結果、要素の数は半分以下になっています。要素の数を減らすため、MyBatis では OGNL ベースの式(expression)を採用しています。
</p>
<ul>
<li>if</li>
<li>choose (when, otherwise)</li>
<li>trim (where, set)</li>
<li>foreach</li>
</ul>
<a id="if"></a><section id="if">
<h2>if</h2>
<p>動的 SQL で最も良く行うのが、次のように条件に応じて where 句に検索条件を追加する処理でしょう。</p>
<div class="verbatim source"><pre class="prettyprint"><select id="findActiveBlogWithTitleLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
</select></pre></div>
<p>このステートメントによって「任意の検索項目」を実現することができます。title を指定しなければ全ての ACTIVE な Blog が返されますが、title を指定した場合は指定したタイトルを持った Blog が返されます(このステートメントでは like 演算子を使っているので、渡された title にワイルドカードを使うこともできます)。</p>
<p>
タイトルと著者の両方を任意の条件としたい場合はどうすれば良いのでしょうか。<br />
ステートメント名を分かりやすいものに変更したら、あとは条件をもう一つ追加するだけです。
</p>
<div class="verbatim source"><pre class="prettyprint"><select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select></pre></div>
</section>
<a id="chooseWhenOtherwise"></a><section id="chooseWhenOtherwise">
<h2>choose, when, otherwise</h2>
<p>全ての条件を適用する代わりに、多くの選択肢の中から一つを選んで適用したいという場合があります。</p>
<p>引き続き上の例を使って、タイトルが指定されたらタイトルのみを条件として検索し、著者が指定されたら著者のみを条件として検索するようにしてみましょう。どちらも指定されなかった場合は注目のブログのみを返すようにしてみましょう(ランダムに選ばれた無意味なリストではなく、管理者が戦略的に選んだリストを返したいという要件があるのでしょう)。</p>
<div class="verbatim source"><pre class="prettyprint"><select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select></pre></div>
</section>
<a id="trimWhereSet"></a><section id="trimWhereSet">
<h2>trim, where, set</h2>
<p>ここまでの例題は動的 SQL の厄介な問題点を都合よく避けていました。もう一度 if の例に戻って、今度は "ACTIVE = 1" も動的な条件に変更してみましょう。</p>
<div class="verbatim source"><pre class="prettyprint"><select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select></pre></div>
<p>どの条件にも一致しない場合はどうなるのでしょうか?その場合は次のような SQL が実行されることになります。</p>
<div class="verbatim source"><pre class="prettyprint">SELECT * FROM BLOG
WHERE</pre></div>
<p>この SQL は構文エラーで失敗するでしょう。もし2番目の条件だけが一致したらどうなるのでしょうか?今度は次の SQL になります。</p>
<div class="verbatim source"><pre class="prettyprint">SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’</pre></div>
<p>これまた構文エラーで失敗するでしょう。動的 SQL の問題は単なる条件分岐だけで解決できるものではありません。自分で書いたことがある方なら、もう二度と書きたくないと思うはずです。</p>
<p>MyBatis は約 90% のケースをうまく処理できる簡単な解決策を提供します。残りの 10% についても、カスタマイズすることで処理できるようになります。上記の例は、一箇所修正するだけで期待通りに動作するようになります。</p>
<div class="verbatim source"><pre class="prettyprint"><select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select></pre></div>
<p><em>where</em> 要素は、内包するタグのどれかが結果を返すときだけ "WHERE" を挿入します。更に、内包するタグから返された結果が "AND" または "OR" で始まっていた場合はこれを削除します。</p>
<p><em>where</em> 要素の動作が期待と異なる場合は、trim 要素を定義することで処理内容をカスタマイズすることができます。</p>
<div class="verbatim source"><pre class="prettyprint"><trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim></pre></div>
<p><em>prefixOverrides</em> 属性にはパイプで区切られたオーバーライド対象の文字列を指定します。ここではスペースにも意味があります。trim 要素の <em>prefixOverrides</em> 属性のリストに含まれる文字列が先頭にあった場合は削除され、<em>prefix</em> 属性で指定された文字列は結果が空でない場合先頭に挿入されます。</p>
<p>動的な update ステートメントのために同じような要素 <em>set</em> が用意されています。<em>set</em> 要素を使うと、アップデート対象の列を動的に追加することができます。例:</p>
<div class="verbatim source"><pre class="prettyprint"><update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update></pre></div>
<p><em>set</em> 要素は、動的に SET キーワードを付加し、余分な末尾のカンマを削除します。</p>
<p>あるいは、<em>trim</em>という要素を使っても同じ効果が得られます。</p>
<div class="verbatim source"><pre class="prettyprint"><trim prefix="SET" suffixOverrides=",">
...
</trim></pre></div>
<p>prefix を追加している点は前の例と同じですが、今回は suffix をオーバーライドしている点に注意してください。</p>
</section>
<section>
<h2>foreach</h2>
<p>動的 SQL で良くあるもう一つの要件は、コレクションの要素をイテレーション処理したいというものです。多くの場合、IN 演算子を使った条件を構築するのが目的です。例:</p>
<div class="verbatim source"><pre class="prettyprint"><select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
<where>
<foreach item="item" index="index" collection="list"
open="ID in (" separator="," close=")" nullable="true">
#{item}
</foreach>
</where>
</select></pre></div>
<p><em>foreach</em> 要素は非常に強力で、イテレーション処理の対象となるコレクションを指定する collection と、ループ内で要素を格納する変数 item、ループ回数を格納する index 変数を宣言することができます。また、開始・終了の文字列とイテレーションの合間に出力する区切り文字を指定することもできます。foreach タグは賢いので、余分な区切り文字を出力することはありません。</p>
<p><span class="label important">NOTE</span> collection には Iterable を実装したオブジェクト(List や Set など)の他に Map や Array を指定することもできます。collection に Iterable または Array を指定した場合、 index で指定した変数にはインデックスの数値、 item で指定した変数にはコレクション、配列の要素が格納されます。Map あるいは Map.Entry のコレクションを指定した場合は index にマップのキー、item にマップの値が格納されます。</p>
<p>XML 設定ファイルと XML Mapper ファイルについての説明はここまでになります。次の章では、Java API について詳しく見ていきます。</p>
</section>
<section>
<h2>script</h2>
<p>For using dynamic SQL in annotated mapper class, <em>script</em> element can be used. For example:</p>
<div class="verbatim source"><pre class="prettyprint">
@Update({"<script>",
"update Author",
" <set>",
" <if test='username != null'>username=#{username},</if>",
" <if test='password != null'>password=#{password},</if>",
" <if test='email != null'>email=#{email},</if>",
" <if test='bio != null'>bio=#{bio}</if>",
" </set>",
"where id=#{id}",
"</script>"})
void updateAuthorValues(Author author);</pre></div>
</section>
<section>
<h2>bind</h2>
<p><code>bind</code> 要素を使うと、OGNL 式の結果を変数に格納し、その変数を SQL 文中で使用することができます。</p>
<div class="verbatim source"><pre class="prettyprint">
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
SELECT * FROM BLOG
WHERE title LIKE #{pattern}
</select></pre></div>
</section>
<section>
<h2>複数データベースのサポート</h2>
<p>databaseIdProvider が設定されている場合、条件式で "_databaseId" 変数が利用可能となります。この変数を使うと、実行時のデータベースに応じてステートメントを使い分けることができます。oracle と db2 で異なる select 文を発行する例を次に挙げておきます。</p>
<div class="verbatim source"><pre class="prettyprint"><insert id="insert">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
<if test="_databaseId == 'oracle'">
select seq_users.nextval from dual
</if>
<if test="_databaseId == 'db2'">
select nextval for seq_users from sysibm.sysdummy1"
</if>
</selectKey>
insert into users values (#{id}, #{name})
</insert>
</pre></div>
</section>
<section>
<h2>ダイナミック SQL 記述言語</h2>
<p>バージョン 3.2 以降、ダイナミック SQL の記述言語が Pluggable になりました。言語ドライバーを記述することで、任意の言語でダイナミック SQL を記述することができます。</p>
<p>カスタムの言語ドライバーを使用する場合、まずは LanguageDriver インターフェイスを実装したクラスを作成します。</p>
<div class="verbatim source"><pre class="prettyprint">public interface LanguageDriver {
ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql);
SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType);
SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType);
}</pre></div>
<p>作成した言語ドライバーをデフォルトとして使用する場合は、mybatis-config.xml に次のような設定を追加します(typeAlias の使用は必須ではありません)。</p>
<div class="verbatim source"><pre class="prettyprint"><typeAliases>
<typeAlias type="org.sample.MyLanguageDriver" alias="myLanguage"/>
</typeAliases>
<settings>
<setting name="defaultScriptingLanguage" value="myLanguage"/>
</settings>
</pre></div>
<p><code>lang</code> 属性を指定することで、特定のステートメントの言語ドライバーのみを変更することもできます。</p>
<div class="verbatim source"><pre class="prettyprint"><select id="selectBlog" lang="myLanguage">
SELECT * FROM BLOG
</select></pre></div>
<p>Mapper インターフェイスを使っている場合は <code>@Lang</code> アノテーションを使います。</p>
<div class="verbatim source"><pre class="prettyprint">public interface Mapper {
@Lang(MyLanguageDriver.class)
@Select("SELECT * FROM BLOG")
List<Blog> selectBlog();
}</pre></div>
<p><span class="label important">NOTE</span> Apache Velocity を使ってダイナミック SQL を記述することができます。MyBatis-Velocity プロジェクトを参照してください。</p>
<p>これまでのセクションで出てきた XML タグは、全てデフォルトの言語ドライバー <code>org.apache.ibatis.scripting.xmltags.XmlLanguageDriver</code> (エイリアスは <code>xml</code> )によって提供されているものです。</p>
</section>
</section>
</main>
</div>
</div>
<hr/>
<footer>
<div class="container-fluid">
<div class="row-fluid">
<p>© 2009–2024
<a href="https://www.mybatis.org/">MyBatis.org</a>
</p>
</div>
</div>
</footer>
<script>
if(anchors) {
anchors.add();
}
</script>
</body>
</html>