Mybatis日常使用小結
ここでは主に自分がプロジェクトで使用した動的sqlといくつかの注意点を紹介します.
動的SQL
概要:ダイナミックSQLはMyBatisの強力な特性の一つです.JDBCや他の類似のフレームワークを使用したことがある場合は、SQL文のスペルが異なる条件に基づいてどれだけ苦しいかを理解することができます.たとえば、スペルに必要なスペースを追加することを忘れないようにし、リストの最後の列名のカンマを削除することに注意してください.ダイナミックSQLを利用して、このような苦痛から完全に抜け出すことができます.
私はここでMybatisの公式サイトのよくある使い方と私自身のプロジェクトの実際のsqlを引用します.
if
動的SQLを使用する最も一般的なシナリオは、条件に基づいてwhere句を含む一部です.例:
この文は、オプションのテキスト検索機能を提供します.「title」が伝わらなければ、「ACTIVE」状態のBLOGはすべて返される.「title」パラメータが入力されると、「title」列が曖昧に検索され、対応するBLOG結果が返されます(注意深い読者は、「title」のパラメータ値に検索マスクまたはワイルドカード文字が含まれる必要があることを発見する可能性があります).
「title」と「author」の2つのパラメータでオプションで検索したい場合はどうすればいいですか?まず、文の名前を副実の名前に変更したいと思います.次に、別の条件を加えるだけでいいです.
choose、when、otherwise
場合によっては、すべての条件を使用したくなくて、複数の条件の中から1つを選択して使用したいだけです.この場合、MyBatisはJavaのswitch文に似たchoose要素を提供します.
やはり上記の例ですが、ポリシーは、「title」が入ったら「title」、「author」が入ったら「author」と検索する場合に変わります.両方が入力されていない場合は、featuredとマークされたBLOGが返されます(これは、管理者が無意味なランダムなBlogを大量に返すよりも、管理者が選択したBlogを返すほうがいいと考えている可能性があります).
trim、where、set
前のいくつかの例は、悪名高い動的SQL問題を適切に解決した.これまでの「if」の例に戻り、今回は「state=『ACTIVE』」を動的条件に設定して、何が起こるか見てみましょう.
一致する条件がなければどうなりますか?最終的にこのSQLは次のようになります.
これにより、クエリが失敗します.2番目の条件だけが一致したらどうなるの?このSQLは次のようになります.
このクエリも失敗します.この問題は単純に条件要素で解決することはできない.この問題はこのように解決しにくいので、解決した人はこのような問題に直面したくない.
MyBatisには、ほとんどのシーンに適した簡単な解決策があります.他のシーンでは、必要に応じてカスタマイズできます.簡単な変更が必要です
自分のプロジェクトで使用される類似のもの:
where要素は、サブ要素が任意の内容を返す場合にのみ「WHERE」句を挿入します.また,句の先頭が「AND」または「OR」の場合,where要素もそれらを除去する.
where要素が望ましくない場合は、trim要素をカスタマイズすることでwhere要素の機能をカスタマイズすることもできます.たとえば、where要素と等価なカスタムtrim要素は、次のとおりです.
prefixOverridesプロパティは、パイプ文字で区切られたテキストシーケンスを無視します(この例ではスペースが必要であることに注意してください).上記の例では、すべてのprefixOverridesプロパティで指定した内容が削除され、prefixプロパティで指定した内容が挿入されます.
文を動的に更新するための類似のソリューションをsetと呼びます.set要素は、更新が必要なカラムを動的に含み、他の更新されていないカラムを無視するために使用できます.例:
この例では、set要素は行の先頭にSETキーワードを動的に挿入し、条件文を使用して列に値を割り当てるときに導入される追加のカンマを削除します.
set要素と等価なカスタムtrim要素を見てみましょう.
接尾辞値の設定を上書きし、接頭辞値をカスタマイズしたことに注意してください.
foreach
ダイナミックSQLのもう1つの一般的な使用シーンは、集合を巡回することです(特にIN条件文を構築する場合).例:
foreach要素の機能は非常に強力で、要素内で使用できる集合項目(item)とインデックス(index)変数を宣言する集合を指定できます.また、先頭と末尾の文字列とコレクションアイテムの反復間の区切り文字を指定することもできます.この要素も誤って余分な区切り記号を追加することはありません.それがどんなにスマートかを見てください.
ヒント:リスト、Setなどの反復可能なオブジェクト、Mapオブジェクト、または配列オブジェクトを集合パラメータとしてforeachに渡すことができます.反復可能なオブジェクトまたは配列を使用する場合、indexは現在の反復のシーケンス番号であり、itemの値は今回の反復で取得された要素です.Mapオブジェクト(またはMap.Entryオブジェクトのセット)を使用する場合、indexはキー、itemは値です.
注意事項
一時的に使用されるsql最適化クエリでは、 where文では、できるだけ大量のデータを排除できる条件を上位に書く に注意ファジイクエリでは'''''を使用せず、'''を使用します.例:
#{...}がsql文に解析されると、変数の外側に自動的に一重引用符''が付けられるので、ここでは%が二重引用符'''を使用する必要があり、一重引用符''は使用できません.そうしないと、結果は表示されません.データベース・フィールドがエンティティー・クラス・フィールドと一致しない場合、selectラベルでresultTypeを使用することはできません.一致しないため、マッピングが必要です.たとえば、 です.
selectラベルのresultMapの値はresultMapラベルのidであり、resultMapのpropertyはエンティティクラスのプロパティを表し、columnはデータベース対応テーブルのカラム名を表します.戻り値が次のタイプの場合:1、基本タイプ:resultType=基本タイプ2、Listタイプ:resultType=Listの要素のタイプ3、Mapタイプ:resultType=map 参照時に異なるタイプの2つのパラメータを転送するにはどうすればいいですか.私が今プロジェクトで使っている最も簡単な方法は です.別名の使用事項:別名を使用すると元の名前は使用できません.別名はスペースまたはasで表に付けることができますが、操作するデータベースがOracleであればスペースしか使用できません.asはOracleの構文に合致しません.またmysqlのdelete文では、別名の使用が変わります. select,insert,update,deleteラベル使用:insert/update/deleteラベルはテンプレートにすぎず、操作時にはSQL文を核心としています.すなわち、insert/update/deleteタグは削除時に共通しますが、クエリー時にselectタグしか使用できません.私たちはどの操作でもラベルを使用することを提唱しています.またmybatisではselectラベルでupdate文を使用しても戻り値がないため、update に変更する必要があります.は、使用項目でtop機能を使用する必要がある場合があります.すなわち、ある列に基づいて並べ替えた後に一番前の行をクエリーし、SQLServerではtopという機能を使用します.mysqlでは、ページング中のlimitで実現できますが、ページング機能とは異なり、sizeパラメータを1つ伝えるだけで、topのデータをクエリーすることができます.例: 挿入時にidを挿入しない場合、データベース内のテーブルのidフィールドは自動的に増加するように設定されます. 自分のプロジェクトに1つのパラメータに基づいてクエリーされたSQLがたくさんある場合は、SQL文に簡略化できます.例:
次のように置換できます.
ここで${column}は直接置き換えられ、#{value}は使用されますか?前処理これにより、同じタスクを完了できます.
この方法は、テーブル名を置き換える場合にも同様に適用されます.
このような方法でユーザーの入力を受け入れ、文パラメータとして使用することは安全ではなく、潜在的なSQL注入攻撃を引き起こす可能性があります.したがって、ユーザがこれらのフィールドを入力することを許可しないか、またはこれらのパラメータを自分でエスケープして検証します.
動的SQL
概要:ダイナミックSQLはMyBatisの強力な特性の一つです.JDBCや他の類似のフレームワークを使用したことがある場合は、SQL文のスペルが異なる条件に基づいてどれだけ苦しいかを理解することができます.たとえば、スペルに必要なスペースを追加することを忘れないようにし、リストの最後の列名のカンマを削除することに注意してください.ダイナミックSQLを利用して、このような苦痛から完全に抜け出すことができます.
私はここでMybatisの公式サイトのよくある使い方と私自身のプロジェクトの実際のsqlを引用します.
if
動的SQLを使用する最も一般的なシナリオは、条件に基づいてwhere句を含む一部です.例:
この文は、オプションのテキスト検索機能を提供します.「title」が伝わらなければ、「ACTIVE」状態のBLOGはすべて返される.「title」パラメータが入力されると、「title」列が曖昧に検索され、対応するBLOG結果が返されます(注意深い読者は、「title」のパラメータ値に検索マスクまたはワイルドカード文字が含まれる必要があることを発見する可能性があります).
「title」と「author」の2つのパラメータでオプションで検索したい場合はどうすればいいですか?まず、文の名前を副実の名前に変更したいと思います.次に、別の条件を加えるだけでいいです.
choose、when、otherwise
場合によっては、すべての条件を使用したくなくて、複数の条件の中から1つを選択して使用したいだけです.この場合、MyBatisはJavaのswitch文に似たchoose要素を提供します.
やはり上記の例ですが、ポリシーは、「title」が入ったら「title」、「author」が入ったら「author」と検索する場合に変わります.両方が入力されていない場合は、featuredとマークされたBLOGが返されます(これは、管理者が無意味なランダムなBlogを大量に返すよりも、管理者が選択したBlogを返すほうがいいと考えている可能性があります).
trim、where、set
前のいくつかの例は、悪名高い動的SQL問題を適切に解決した.これまでの「if」の例に戻り、今回は「state=『ACTIVE』」を動的条件に設定して、何が起こるか見てみましょう.
一致する条件がなければどうなりますか?最終的にこのSQLは次のようになります.
SELECT * FROM BLOG
WHERE
これにより、クエリが失敗します.2番目の条件だけが一致したらどうなるの?このSQLは次のようになります.
SELECT * FROM BLOG
WHERE
AND title like ‘someTitle’
このクエリも失敗します.この問題は単純に条件要素で解決することはできない.この問題はこのように解決しにくいので、解決した人はこのような問題に直面したくない.
MyBatisには、ほとんどのシーンに適した簡単な解決策があります.他のシーンでは、必要に応じてカスタマイズできます.簡単な変更が必要です
自分のプロジェクトで使用される類似のもの:
where要素は、サブ要素が任意の内容を返す場合にのみ「WHERE」句を挿入します.また,句の先頭が「AND」または「OR」の場合,where要素もそれらを除去する.
where要素が望ましくない場合は、trim要素をカスタマイズすることでwhere要素の機能をカスタマイズすることもできます.たとえば、where要素と等価なカスタムtrim要素は、次のとおりです.
...
prefixOverridesプロパティは、パイプ文字で区切られたテキストシーケンスを無視します(この例ではスペースが必要であることに注意してください).上記の例では、すべてのprefixOverridesプロパティで指定した内容が削除され、prefixプロパティで指定した内容が挿入されます.
文を動的に更新するための類似のソリューションをsetと呼びます.set要素は、更新が必要なカラムを動的に含み、他の更新されていないカラムを無視するために使用できます.例:
update Author
username=#{username},
password=#{password},
email=#{email},
bio=#{bio}
where id=#{id}
この例では、set要素は行の先頭にSETキーワードを動的に挿入し、条件文を使用して列に値を割り当てるときに導入される追加のカンマを削除します.
set要素と等価なカスタムtrim要素を見てみましょう.
...
接尾辞値の設定を上書きし、接頭辞値をカスタマイズしたことに注意してください.
foreach
ダイナミックSQLのもう1つの一般的な使用シーンは、集合を巡回することです(特にIN条件文を構築する場合).例:
foreach要素の機能は非常に強力で、要素内で使用できる集合項目(item)とインデックス(index)変数を宣言する集合を指定できます.また、先頭と末尾の文字列とコレクションアイテムの反復間の区切り文字を指定することもできます.この要素も誤って余分な区切り記号を追加することはありません.それがどんなにスマートかを見てください.
ヒント:リスト、Setなどの反復可能なオブジェクト、Mapオブジェクト、または配列オブジェクトを集合パラメータとしてforeachに渡すことができます.反復可能なオブジェクトまたは配列を使用する場合、indexは現在の反復のシーケンス番号であり、itemの値は今回の反復で取得された要素です.Mapオブジェクト(またはMap.Entryオブジェクトのセット)を使用する場合、indexはキー、itemは値です.
注意事項
一時的に使用されるsql最適化
select * from table
などのクエリに使用される場合、*をすべてのカラム名に変更すると、*
がマッピングによってカラム名に変換される時間が消費されないようにすることが望ましい.
#{...}がsql文に解析されると、変数の外側に自動的に一重引用符''が付けられるので、ここでは%が二重引用符'''を使用する必要があり、一重引用符''は使用できません.そうしないと、結果は表示されません.
selectラベルのresultMapの値はresultMapラベルのidであり、resultMapのpropertyはエンティティクラスのプロパティを表し、columnはデータベース対応テーブルのカラム名を表します.
// dao
Integer updateType(@Param("id") Long id, @Param("typeName") String typeName);
// mapper SQL
update hi_type t set t.type_name = #{typeName} where
t.id = #{id}
// delete ,
delete t from hi_type t where t.id = #{id}
//
delete from hi_type t where t.id = #{id}
@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);
@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);
@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);
// "findByXxx"
次のように置換できます.
@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);
ここで${column}は直接置き換えられ、#{value}は使用されますか?前処理これにより、同じタスクを完了できます.
User userOfId1 = userMapper.findByColumn("id", 1L);
User userOfNameKid = userMapper.findByColumn("name", "kid");
User userOfEmail = userMapper.findByColumn("email", "[email protected]");
この方法は、テーブル名を置き換える場合にも同様に適用されます.
このような方法でユーザーの入力を受け入れ、文パラメータとして使用することは安全ではなく、潜在的なSQL注入攻撃を引き起こす可能性があります.したがって、ユーザがこれらのフィールドを入力することを許可しないか、またはこれらのパラメータを自分でエスケープして検証します.