記事ディレクトリ
1. 追加、削除、変更操作
1.1 変更操作
パスワード変更関数を実現し、変更された行 (影響を受ける行) の数を返します。
まず、パスワード、ユーザー ID、古いパスワード、新しいパスワードを変更するために渡す必要があるパラメーターを決定します。
インターフェースでメソッドを宣言します。
// 修改密码
int updatePassword(@Param("id") Integer id,
@Param("password") String password,
@Param("newPassword") String newPassword);
XMLでの実装方法:
ヒント: 追加、削除、変更のため、更新タグに resultType を追加する必要はありません。そのような操作では結果セットを返す必要はありませんが、成功したか ID を知る必要があるだけです。resultType は属性を指定します。戻り結果のタイプ。MyBatis に結果セットを Java オブジェクトに変換する方法を指示します。
<update id="updatePassword">
update userinfo set password=#{newPassword}
where id=#{id} and password=#{password}
</update>
単体テスト:
@Transactional //正常执行不污染数据库
@Test
void updatePassword() {
int result = userMapper.updatePassword(1, "admin", "123456");
System.out.println("修改:" + result);
}
ヒント: @Transactional (トランザクション)、このアノテーションを追加すると、データベースが変更されるのを防ぎ、SQL ステートメントの実行時にトランザクションを開始し、ロールバック (ロールバック) 前に実行が完了するのを待つことができます。
1.2 削除操作
ユーザーを削除し、影響を受ける行の数を返します。
インターフェースでメソッドを宣言します。
// 删除密码
int delById(@Param("id") Integer id);
XMLでの実装方法:
<delete id="delById">
delete from userinfo where id=#{id}
</delete>
単体テスト:
@Transactional
@Test
void delById() {
int id = 1;
int result = userMapper.delById(1);
System.out.println("删除结构" + result);
}
1.3 追加操作
1.3.1 影響を受ける行の数を返す
インターフェースでメソッドを宣言します。
// 添加用户
int addUser(UserEntity user);
XMLでの実装方法:
<insert id="addUser">
insert into userinfo(username, password) values(#{username},#{password})
</insert>
単体テスト:
@Test
void addUser() {
UserEntity user = new UserEntity();
user.setUsername("张三");
user.setPassword("11111");
int result = userMapper.addUser(user);
System.out.println(result);
}
1.3.2 戻りID
Interface での宣言方法は以前と同じです。
int addUserGetId(UserEntity user);
挿入タグ内に、userGenerateKeys="true"を追加して主キーを生成し、keyProperty="id"を追加して主キーを id に設定します。
<insert id="addUserGetId" useGeneratedKeys="true" keyProperty="id">
insert into userinfo(username, password) values(#{username},#{password})
</insert>
単体テスト:
この時点で、MyBatis は getId をオブジェクトに自動的に追加します。
@Test
void addUserGetId() {
UserEntity user = new UserEntity();
user.setUsername("李四");
user.setPassword("22222");
int result = userMapper.addUserGetId(user);
System.out.println("添加行数" + result);
System.out.println("Id 为: " + user.getId());
}
2. クエリ操作
2.1 単一テーブルクエリ
ユーザーIDに応じてユーザー情報を問い合わせる機能を実現します。
まず、インターフェイスでメソッドを宣言します。
ヒント: @Param を追加しないと、コンピューターによってはパラメーターが見つからないため、一律に @Param を追加します。
// 根据 id 查询用户对象 , @Param 相当于给参数起名, 如果名称不一致会报错
UserEntity getUserById(@Param("id") Integer id);
XMLでSQLコマンドを実装する
<select id="getUserById" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where id=${id}
</select>
データベースを汚染しないように、単体テストを使用してコードをテストします。
@SpringBootTest //声明当前单元测试的类是运行在 SpringBoot 当中的.
class UserMapperTest {
@Autowired //属性注入
private UserMapper userMapper;
@Test
void getUserById() {
UserEntity user = userMapper.getUserById(1);
System.out.println(user);
}
}
実行結果は次のとおりです。データベース内の結果と一致しています。
2.1.1 パラメータのプレースホルダ ${} および #{}
- ${} 文字の直接置換
- #{} プレースホルダーのプリコンパイル処理
直接置換とは、 MyBatis が ${} を処理するときに、変数の値に直接置換されることを意味します。
前処理とは、 MyBatis が処理時に #{} を ? に置き換え、PreparedStatement の set メソッドを呼び出して値を割り当てることを意味します。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-LrF7S6xc-1686045145549) (C:/Users/86178/AppData/Roaming/Typora/) typora-user-images/image-20230511093437168.png)]
例えば、日常生活においては、毎朝、さまざまなショッピングモールのドアを開ける前に、各ポジションの従業員を入場させ、従業員が全員揃うまで待ってから営業を開始することで、一部の顧客が購入できないことを防ぐためです。 0元で、この操作は前処理に相当し、顧客が波乱の海で釣りをし、早めにモールに入場した場合、クロックインシステムによって認識され、直接交換の場合、顧客は入場できますモールはいつでも監視機構なしで運営されており、この時点で異常が発生する可能性があります。
2.1.2 SQLインジェクションの問題
SQLインジェクションでよくある場面としてログインがありますが、次にSQLインジェクションを導入するためにブログシステムのログイン機能を実装します。
ログイン メソッドはInterfaceで宣言されます。
@Mapper
public interface UserMapper {
// 登录方法
UserEntity login(UserEntity user);
}
XMLでの実装方法:
ユーザー名とパスワードは String 型の変数であり、${} は変数を直接置き換えるため、SQL ステートメントの正確性を確保するために、手動で一重引用符を追加します。
<select id="login" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username='${username}' and password='${password}'
</select>
ヒント: メソッドのパラメータとして渡されたオブジェクトは、XML で実装されると、MyBatis によって属性名が自動的にマップされます。
単体テスト:
@Test
void login() {
String username = "admin";
String password = "admin";
UserEntity inputUser = new UserEntity();
inputUser.setUsername(username);
inputUser.setPassword(password);
UserEntity user = userMapper.login(inputUser);
System.out.println(user);
}
結果は正しいです
しかし、次のように入力すると、「 ' または 1='1」;
@Test
void login() {
String username = "admin";
String password = " ' or 1='1";
UserEntity inputUser = new UserEntity();
inputUser.setUsername(username);
inputUser.setPassword(password);
UserEntity user = userMapper.login(inputUser);
System.out.println(user);
}
ユーザーの情報は引き続き照会されます。これは、いわゆるSQL インジェクションです。
なぜこのようなエラーが発生するのでしょうか? これは、プログラムの入力パラメータが SQL コマンドとして実行されるためです。
SQL の自動暗黙的な型変換では、1='1'が正しい必要があります。
SQLインジェクションを防ぐにはどうすればよいでしょうか?
#{} は事前実行できるので、中括弧内のパラメータはそのビューの値であり、等しい場合は見つかりますが、等しくない場合は見つかりませんので問題ありませんSQL ステートメントへのスプライスの処理。
<select id="login" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username=#{username} and password=#{password}
</select>
#{} はSQL インジェクションを防ぐだけでなく、パラメーターの型に応じて一重引用符を自動的に追加するため、${} は役に立たず、結果はそうではありません。多くの文字列スプライシング シナリオはこれに違いないため、データ セキュリティの観点から、は最後の手段ではありません。使用する必要がある場合は、入力パラメータが列挙可能であることを確認し、変数を置き換える前にチェックしてください。
2.1.3 ${} の利点
タオバオや京東などの電子商取引プラットフォームを閲覧する際、さまざまな属性で並べ替える必要がある場合がありますが、そのような機能を実装する場合、オプションのパラメータが多数あるため、ハードコーディングできず、結合する必要があります。後続のユーザーのオプションに従って。
インターフェースでの宣言:
List<UserEntity> getAllBySort(@Param("Sort") String Sort);
XML で実装:
<select id="getAllBySort" resultType="com.example.demo.entity.UserEntity">
select * from userinfo order by id ${Sort}
</select>
単体テストの実行:
@Test
void getAllBySort() {
List<UserEntity> userSort = userMapper.getAllBySort("desc");
for(UserEntity user: userSort){
System.out.println(user);
}
}
${} を使用すると、必要な文字列に直接置き換えることができますが、#{} を使用すると、渡された値によって String に一重引用符が追加され、SQL ステートメント エラーが発生するため、並べ替えクエリを実装できません。
2.1.4 いいねクエリ
ファジー クエリの関数を実装する必要がある場合、#{} は受信値が String 型であることを検出し、一対の一重引用符を追加します。これは '%'username'%' となり、エラーが発生します。 SQL ステートメント。
<select id="getListByName" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username like '%#{username}%'
</select>
解決:
MySQL の組み込み関数 concat 文字列連結を使用すると、この問題を解決できます。
<select id="getListByName" resultType="com.example.demo.entity.UserEntity">
select * from userinfo where username like concat('%','#{username}','%')
</select>
インデックスの無効化:
ファジー クエリを作成するには、大まかに 3 つの方法があります。
- 開ける%
- %開ける
- %開ける%
最初の 1 つだけがインデックスをトリガーし、他の 2 つはインデックスの失敗を引き起こします。
2.2 複数テーブルのクエリ
影響を受ける行の数を返すだけでよい追加、削除、および変更操作では、戻り値の型を設定する必要はありません。
<update id="updatePassword">
update userinfo set password=#{newPassword}
where id=#{id} and password=#{password}
</update>
<delete id="delById">
delete from userinfo where id=#{id}
</delete>
<insert id="addUser">
insert into userinfo(username, password) values(#{username},#{password})
</insert>
ただし、クエリ操作の場合は、ユーザー名を検索している場合でも、戻り値の型を設定する必要があります。設定しないとエラーが報告されます。これは、MyBatis がデータベースから取得した結果セットを Java オブジェクトにマップできないためです。
2.2.1 戻り値の型 resultType
MyBatis の resultType はクエリ結果のタイプを指定するために使用され、データベースから取得した結果セットを Java オブジェクトにマップするように MyBatis に指示します。
<select id="getUser" resultType="com.example.User"> select * from user where id = #{id} </select>
上記の例では、resultType はクエリ結果のタイプが com.example.User であることを指定しており、クエリ結果が User オブジェクトに変換されることを示しています。
ヒント: resultType は単一結果クエリにのみ適用されます。複数結果クエリを実行する必要がある場合は、resultMap を使用してクエリ結果のマッピング関係を指定する必要があります。
2.2.2 辞書マッピングの resultMap を返す
resultMap の使用シナリオ
- フィールド名と属性名は異なります。resultMap を使用してマッピングを構成できます。
- 1 対 1 および 1 対多では、resultMap を使用してデータのマッピングとクエリを実行できます。
データベースのフィールド名が Java オブジェクトのフィールド名と一致しない場合、resultType が直接使用されるとエラーが報告されます。resultType は指定された型に従ってデータベースと Java オブジェクトの間で直接マッピングされるため、そうでない場合はエラーが報告されます。一致するものが見つかった場合、マッピングは失敗します。
データベース内のパスワードがpassword、Javaオブジェクトがpwdであるとします。
XMLでresultMapを設定する
各 resultMap にはデフォルトで 2 つの属性があります。idは、MyBatis 内で異なる resultMap を区別するために使用される resultMap の一意の識別子を表します (任意の場合もあります)。type はエンティティクラスのタイプを表し、property は Java オブジェクトの属性です。列はデータベースです。 内の対応するフィールド名。
<resultMap id="BaseMap" type="com.example.demo.entity.UserEntity">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="pwd" column="password"></result>
<result property="createtime" column="createtime"></result>
</resultMap>
指定されたメソッドの resultMap を設定します
<select id="getListByName" resultMap="BaseMap">
select * from userinfo where username like concat('%',#{username},'%')
</select>
暴力的な方法
XML で SQL ステートメントに別名を付けることもできます
<select id="getListByName" resultMap="BaseMap">
select id,username,password as pwd from userinfo where username like concat('%',#{username},'%')
</select>
2.2.3 複数テーブルの結合クエリ
複数のテーブルをクエリするときに、クラスに別のオブジェクトが含まれている場合、resultType は含まれているオブジェクトを見つけることができません。
複数のテーブルをクエリする場合、通常は左/右結合を使用してテーブルを接続し、テーブルをプライマリ テーブルとセカンダリ テーブルに分割できます。
記事テーブルの詳細を問い合わせる
記事テーブルの詳細をクエリしたいが、著者 ID しか見つからない場合、著者名を見つけるには、著者テーブルと一緒にクエリを実行する必要があります。
まずユーザーテーブルのエンティティクラスを作成します
@Data
public class ArticleInfo {
private int id;
private String title;
private String content;
private LocalDateTime createTime;
private int uid;
private int rcount;
private int state;
}
拡張情報を格納する拡張クラスvo(view object)を作成する
継承により、元のエンティティ クラスの属性をより簡単に継承できます。ユーザー名属性を元のエンティティ クラスに直接追加するのは便利に見えますが、プログラム設計の 1 つの原則に従っていません。後から数百または数千の属性を拡張すると、汚染されてしまいます。エンティティクラス
@Data
public class ArticleInfoVO extends ArticleInfo {
private String username;
@Override
public String toString() {
return "ArticleInfoVO{" +
"username='" + username + '\'' +
"} " + super.toString();
}
}
注意: 拡張クラスは toString() メソッドを書き直す必要があります。そうしないと、lombok の toString() はデフォルトで現在のクラスの属性のみを出力します。
MyBatis インターフェイス宣言メソッドを作成します。
@Mapper
public interface ArticleMapper {
// 查询文章详情
ArticleInfoVO getDetail(@Param("id") Integer id);
}
MyBatis で XML を作成する際にこのメソッドを実装します。
<?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.demo.mapper.ArticleMapper">
<select id="getDetail" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select a.*,u.username from articleinfo a
left join userinfo u on u.id=a.uid
where a.id=#{id}
</select>
</mapper>
単体テスト:
@SpringBootTest
class ArticleMapperTest {
@Autowired
ArticleMapper articleMapper;
@Test
void getDetail() {
ArticleInfoVO articleInfoVO = articleMapper.getDetail(1);
System.out.println(articleInfoVO);
}
}
ユーザーのすべての記事をクエリする
まず、誰がメイン テーブルで誰が補助テーブルであるかを明確に分析します。分析を通じて、クエリ結果のほとんどがarticle テーブルのフィールドであるため、article テーブルがメイン テーブルであることがわかります。
Mybatisのインターフェース宣言を作成する
@Mapper
public interface ArticleMapper {
//查询用户的所有文章
List<ArticleInfoVO> getListByUid(@Param("uid") Integer uid);
}
このメソッドをMybatisのxmlファイルに実装します。
<select id="getListByUid" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select a.*,u.username from articleinfo a
left join userinfo u on a.uid=u.id
where a.uid=#{uid}
</select>
単体テストを実行する
@Test
void getListByUid() {
Integer uid = 1;
List<ArticleInfoVO> list = articleMapper.getListByUid(uid);
// 使用并行的方式打印用户的信息
// list.stream().parallel().forEach(System.out::println);
for (ArticleInfoVO list1: list) {
System.out.println(list1);
}
}
3. 複雑なクエリ - 動的 SQL
動的 SQL は通常、ユーザー入力または検索関数、動的並べ替え、動的フィルターなどの他の実行時条件に基づいて SQL ステートメントを生成する必要があるシナリオで使用されます。動的 SQL は、プログラミング言語の文字列結合や条件判断などの構文を使用して実装でき、つまり、SQL ステートメント内で条件付き結合が可能です。
3.1 ifタグ
ユーザーを追加するときに、次の問題が発生する可能性があります。
フィールドの追加は必須フィールドと非必須フィールドの 2 種類に分かれており、このうち ID は必須フィールドで、残りのフィールドは非必須フィールドです。ユーザーを追加すると、写真フィールドが無制限であるため、予期しない結果になる可能性があります。この状況を防ぐために、ラベルを使用して解決できます。
<insert id="addUser2">
insert into userinfo(username,password
<if test="photo!=null and photo!=''">
,photo
</if>
) values(#{username},#{password}
<if test="photo!=null and photo!=''">
,#{photo}
</if>
)
</insert>
タグ内のテストの内容はデータベース内のフィールドではなく、Mybatis によって実行される受信オブジェクトの属性です。入力された写真フィールドが null または空の場合、結合されません。
それがデータベースのフィールドであるか、オブジェクトの属性であるかをどのように区別するのでしょうか?
#{photo} と同様に、特殊文字で変更されているかどうかを確認してください。特殊文字で変更されるものはオブジェクトの属性である必要があり、写真はデータベース フィールドです。
単体テスト:
@Transactional
@Test
void addUser2() {
String username = "liliu";
String password = "123456";
String photo = "";
UserEntity user = new UserEntity();
user.setUsername(username);
user.setPassword(password);
user.setPhoto(photo);
int result = userMapper.addUser2(user);
System.out.println("添加: "+result);
}
明らかに、写真フィールドに入ると空であり、SQL には連結がありません。
3.2 トリムラベル
すべての属性がオプションの場合は、タグとタグを組み合わせて複数のフィールドを動的に生成することを検討してください。
タグには次の属性があります。
- prefix: perfix の値が接頭辞として付けられたステートメント ブロック全体を示します。
- suffix: suffix の値を接尾辞として持つステートメント ブロック全体を示します。
- prefixOverrides: ステートメント ブロック全体から削除されるプレフィックスを示します。
- suffixOverrides: ステートメント ブロック全体から削除されるサフィックスを示します。
前述のユーザー追加の問題ですが、すべてのフィールドが必須フィールドではない場合、ラベルに従って記述すると、カンマの位置をどのように設定しても、必然的に多少のカンマが発生します。
<insert id="addUser3">
insert into userinfo(
<if test="username!=null and username!=''">
username,
</if>
<if test="password!=null and password!=''">
password,
</if>
<if test="photo!=null and photo!=''">
photo
</if>
) values(
<if test="username!=null and username!=''">
#{username},
</if>
<if test="password!=null and password!=''">
#{password},
</if>
<if test="photo!=null and photo!=''">
#{photo}
</if>
)
</insert>
したがって、それに合った書き方に変更することができます
次のコードで Trim を記述すると、ステートメント ブロック全体の最後のコンマを削除し、括弧を追加できます。
- プレフィックス構成に基づいて、(
- サフィックス設定に基づいて、最後に) を追加します
- suffixOverrides に基づく構成では、最後のカンマが削除されます
<insert id="addUser3">
insert into userinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username!=null and username!=''">
username,
</if>
<if test="password!=null and password!=''">
password,
</if>
<if test="photo!=null and photo!=''">
photo,
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username!=null and username!=''">
#{username},
</if>
<if test="password!=null and password!=''">
#{password},
</if>
<if test="photo!=null and photo!=''">
#{photo},
</if>
</trim>
</insert>
単体テストを実行します。
@Transactional
@Test
void addUser3() {
String username = "liliu";
String password = "123456";
String photo = "";
UserEntity user = new UserEntity();
user.setUsername(username);
user.setPassword(password);
// user.setPhoto(photo);
int result = userMapper.addUser3(user);
System.out.println("添加: "+result);
}
ラベルを追加せずにユーザー名とパスワードのフィールドのみを追加する場合、パスワードの後に余分なカンマが必要になるため、SQL ステートメントでエラーが発生しますが、これを使用すると正常に実行されます。
3.3 whereタグ
where タグは、SQL ステートメント内に where 句を生成するために使用され、その機能は、指定された条件に従ってデータをフィルター処理することです。
ID とタイトルで記事をクエリする
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
where
<trim suffixOverrides="and">
<if test="id!=null and id > 0">
id=#{id} and
</if>
<if test="title!=null and title!=''">
title like concat('%',title,'%')
</if>
</trim>
</select>
上記のコードでパラメータを渡す場合は、 1. id が渡され、title が渡されない 2. id が渡されず、title が渡される 3. id が渡され、title が渡される 4. id が渡されない 4 つのケースがあります。渡されましたが、タイトルは渡されません。
テストを通じて、4 番目のケースでは、where 条件が空の場合、SQL ステートメント エラーが発生することがわかりました。この状況には 2 つの解決策があります。
解決策 1: 1=1 の解決策
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
where 1=1
<trim prefixOverrides="and">
<if test="id!=null and id > 0">
and id=#{id}
</if>
<if test="title!=null and title!=''">
and title like concat('%',title,'%')
</if>
</trim>
</select>
コード コンパイラの 1=1 はパフォーマンスを消費せずに最適化されますが、多くの企業のコード仕様ではこれを推奨していません。
解決策 2: トリム タグのプレフィックスはどこですか
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
<trim prefix="where" suffixOverrides="and">
<if test="id!=null and id > 0">
id=#{id} and
</if>
<if test="title!=null and title!=''">
title like concat('%',title,'%')
</if>
</trim>
</select>
コードがトリムで生成されると、プレフィックスとサフィックスが追加されます。トリムにコードがない場合は、プレフィックスとサフィックスが追加されます。
しかし、タグを使用すると、接頭辞や接尾辞、前後にカンマを追加するなど、多くの懸念事項が生じることがわかります...
このときタグを使えばこの悩みは完璧に解決できます。
解決策 3: ラベル
<select id="getListByIdOrTitle" resultType="com.example.demo.entity.vo.ArticleInfoVO">
select * from articleinfo
<where>
<if test="id!=null and id > 0">
id=#{id}
</if>
<if test="title!=null and title!=''">
and title like concat('%',title,'%')
</if>
</where>
</select>
ラベルに加えて、より簡潔で便利になり、最初のキーワードとキーワードを削除することもできますが、ラベルでは最後のキーワードは削除されないことに注意してください。
3.4 タグを設定する
set タグは where タグと同じ機能を持ちますが、set タグは update で使用する必要がある点が異なります。
<update id="updatename">
update user
<set>
<if test="username!=null">
username=#{username},
</if>
<if test="password!=null">
password=#{username},
</if>
<if test="sex!=null">
sex=#{sex},
</if>
<if test="birth!=null">
birth=#{birth},
</if>
</set>
where id = #{id}
</update>
最後のキーワードであるタグは削除することもできますが、設定されたタグにパラメータが無い場合はSQL文エラーが発生するので注意が必要です。オブジェクトが空であるか、コントローラー層にない場合、両方が空の場合は実行されません。
3.5 foreachタグ
このタグは、コレクションをトラバースするときに使用できます。このタグには次の共通のプロパティがあります。
- コレクション: 渡されたコレクションの名前を格納します。 List 、 Map 、 Set
- item: トラバース中に各オブジェクトを保存します。
- open:foreach のプレフィックスは何ですか?
- close: foreach のサフィックスは何ですか
- separator: 各レイヤーの走査の区切り文字は何ですか
コレクション内のIDに従って記事を一括削除します
<delete id="deleteByIdS">
delete from articleinfo
where id in
<foreach collection="ids" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
ラベルの実行結果
delete from Articleinfo
where id in
(id1, id2 , id3 , id4)
ヒント: コードを実行する前に、コントローラ層で受け取ったパラメータが空かどうかを判断する必要があり、空であれば実行されず、そうでなければライブラリを削除して逃げるのと同じです。
ername}、
パスワード=#{ユーザー名}、
性別=#{性別}、
誕生=#{誕生}、
ID = #{id}
<set> 标签可以去除 , 最后面的关键字 , 但必须注意的是如果 set 标签中没有参数会出现 sql 语句错误. 因此执行该方法之前必须在 controller 层先判断一下传入对象的参数都为是否为空 , 如果都为空则不执行.
---
### 7.5 foreach 标签
对集合遍历时可以使用该标签. <foreach> 标签有如下常用属性:
- colletion: 存放传递过来集合的名称 , List , Map , Set
- item: 存放遍历时的每一个对象.
- open: foreach 的前缀是什么
- close: foreach 的后缀是什么
- separator: 每一层遍历的分隔符是什么
根据集合中的 id 批量删除文章
```xml
<delete id="deleteByIdS">
delete from articleinfo
where id in
<foreach collection="ids" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
ラベルの実行結果
delete from Articleinfo
where id in
(id1, id2 , id3 , id4)
ヒント: コードを実行する前に、コントローラ層で受け取ったパラメータが空かどうかを判断する必要があり、空であれば実行されず、そうでなければライブラリを削除して逃げるのと同じです。