詳解MyBatis特性之動態(tài)SQL
前言
動態(tài) SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。利用動態(tài) SQL,可以徹底擺脫這種痛苦。具體的定義大家可以參考官方文檔MyBatis動態(tài)SQL。這篇文章我們將結合動態(tài)SQL完成更加復雜的 SQL 操作。
增加操作
想必大家肯定遇到過注冊某個賬號的時候需要輸入自己的相關信息,其中這些信息包括:必填信息和非必填信息,對于這些必填信息,我們只需要在創(chuàng)建表的時候將這個字段設置為非 null 就可以了,而對于那些非必選的選項,我們又該如何定義呢?
這時就需要我們使用動態(tài)標簽來判斷了,對于這些可以傳遞值和可以不傳遞值的字段,我們可以使用 <if>
標簽來修飾:
@Insert("insert into userinfo(username,`password`,age," + "<if test='gender!=null'>gender,</if>" + "phone)" + "values(#{username},#{password},#{age}," + "<if test='gender!=null'>gender,</if>" + "#{phone})") public Integer insertByCondition(UserInfo userInfo);
<if test="">123</if>
這個標簽中 test 表示的是判斷,當 test 參數中的判斷為真時,那么這個標簽的結果就為 <if> </if>
標簽之間的代碼塊,在這里就是123;如果 test 中的代碼塊的判斷為假的時候,那么這個<if>
標簽的結果就是空。
@Test void insertByCondition() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("彭于晏"); userInfo.setPassword("123456"); userInfo.setAge(18); userInfo.setPhone("132131231"); int ret = userInfoMapper.insertByCondition(userInfo); log.info(ret + "被更新"); }
然后我們調用這個方法的時候,可以不為 gender 字段傳遞值,如果不傳遞值,那么這個字段的值就為創(chuàng)建表時定義的默認值,也可以傳遞值。然后我們運行一下看能達到效果嗎?
這里為什么會報錯呢?因為 <if>
標簽是屬于 JavaScript 的,所以我們需要使用到 <script>
標簽。
@Insert("<script>" + "insert into userinfo(username,`password`,age," + "<if test='gender!=null'>gender,</if>" + "phone)" + "values(#{username},#{password},#{age}," + "<if test='gender!=null'>gender,</if>" + "#{phone})" + "</script>") public Integer insertByCondition(UserInfo userInfo);
有人會問了,使用 <if>
標簽和不使用作用不是一樣的嗎?對于當前插入數據操作作用是一樣的,但是如果我們進行的是修改操作的話,因為我們不知道用戶需要修改什么信息,所以我們在寫修改操作的話,就需要將所有的字段的修改操作都寫上,但是如果我們不使用 <if>
標簽的話,并且用戶在修改的時候,某個信息沒有修改話,后端SQL預處理之后是這樣的:update userinfo set username=?, password=?, gender=?, phone=? where username=?
,前端傳遞來的參數是這樣的:null, null, null, 123456, 小美
,也就是用戶只是修改了電話號碼這個字段,但是因為沒有使用 <if>
標簽,所以其他的字段就會被修改為 null,這就會出現問題了。而使用 <if>
標簽就會這樣處理:update userinfo phone=? where username=?
,參數傳遞:123456, 小美
。
上面是使用注解的方式來實現 MyBatis 的,實現 MyBatis 不僅可以通過注解來實現,也可以通過 XML 的格式實現。我們看看 XML 如何實現動態(tài) SQL。
首先我們需要告訴 MyBatis 我們的 xml 文件在哪里:
mybatis: mapper-locations: classpath:mapper/**Mapper.xml
然后在 XML 文件中寫入下面代碼,SQL 語句寫在 <mapper>
標簽中。
<?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.mybatis20240101.mapper.UserInfoMapper"> </mapper>
這里 namespace 的值是我們是使用了 MyBatis 框架操作數據庫的類的全限定類名稱。
<insert id="insertByCondition"> insert into userinfo(username,`password`,age, <if test="gender!=null"> gender, </if> phone) values(#{username},#{password},#{age}, <if test="gender!=null"> #{gender}, </if> #{phone}) </insert>
因為 XML 文件本身就支持 JavaScript,所以我們這里不需要添加 <script>
標簽,不僅如此,使用 XML 文件的方式寫 MyBatis 還會有提示,所以書寫動態(tài) SQL 建議使用 XML 文件格式。
但是使用 <if>
標簽也會存在問題,假設我們將 phone 字段也設置為動態(tài)的,并且在傳遞值的時候不傳遞 phone 的話就會出現問題。
<insert id="insertByCondition"> insert into userinfo(username,`password`,age, <if test="gender!=null"> gender, </if> <if test="phone!=null"> phone </if>) values(#{username},#{password},#{age}, <if test="gender!=null"> #{gender}, </if> <if test="phone!=null"> #{phone} </if>) </insert>
@Test void insertByCondition() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("彭于晏"); userInfo.setPassword("123456"); userInfo.setAge(18); //userInfo.setPhone("123456"); int ret = userInfoMapper.insertByCondition(userInfo); log.info(ret + "被更新"); }
可以看到,因為 phone 是我們定義增加操作時候的最后一個字段,在這里將 phone 設置為動態(tài)的,并且在傳遞值的時候沒有傳遞 phone,那么在拼接 SQL 的時候 insert into userinfo() values()
中兩個括號中一定會是以逗號結尾,那么這樣的 SQL 就是不和規(guī)則的 SQL,那么如何解決呢?這里就需要用到 <trim>
標簽了。
<trim>標簽
trim 標簽在 MyBatis 中主要用于處理 SQL 語句,以去除多余的關鍵字、逗號,或者添加特定的前綴和后綴。這在進行選擇性插入、更新、刪除或條件查詢等操作時非常有用。
trim 標簽有以下屬性:
- prefix:表?整個語句塊,以prefix的值作為前綴
- suffix:表?整個語句塊,以suffix的值作為后綴
- prefixOverrides:表?整個語句塊要去除掉的前綴
- suffixOverrides:表?整個語句塊要去除掉的后綴
因為我們這里是語句塊末尾出現了多余的逗號,所以我們配置 suffixOverrides 屬性來刪除多余的逗號。
<insert id="insertByCondition"> insert into userinfo <trim prefix="(" suffix=")" suffixOverrides=","> username,`password`,age, <if test="gender!=null"> gender, </if> <if test="phone!=null"> phone </if> </trim> values <trim prefix="(" suffix=")" suffixOverrides=","> #{username},#{password},#{age}, <if test="gender!=null"> #{gender}, </if> <if test="phone!=null"> #{phone} </if> </trim> </insert>
查詢操作
大家在肯定在網上買過手機吧,當我們買手機的時候,往往會加上一些限制條件。
而這種加上限制條件的查詢就可以看成是 select 后面加了 where 語句,但是由于不知道用戶需要加上多少查詢時候的限制條件,所以這里就可以使用到動態(tài) SQL。
UserInfo selectByCondition(UserInfo userInfo);
<select id="selectByCondition" resultType="com.example.mybatis20240101.model.UserInfo"> select * from userinfo where <if test="username!=null"> username=#{username} </if> <if test="password!=null"> and password=#{password} </if> <if test="age!=null"> and age=#{age} </if> <if test="phone!=null"> and phone=#{phone} </if> </select>
@Test void selectByCondition() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("彭于晏"); userInfo.setPhone("34567"); log.info(userInfoMapper.selectByCondition(userInfo).toString()); }
當然這里也會出現問題,就是當第一個 where 子句沒有傳遞值的話,那么 where 子句中就會多一個 and 在開頭。
@Test void selectByCondition() { UserInfo userInfo = new UserInfo(); //userInfo.setUsername("彭于晏"); userInfo.setPhone("34567"); log.info(userInfoMapper.selectByCondition(userInfo).toString()); }
為了解決問題,可以使用 <trim>
標簽刪除前面多余的 and:
<select id="selectByCondition" resultType="com.example.mybatis20240101.model.UserInfo"> select * from userinfo where <trim prefixOverrides="and"> <if test="username!=null"> username=#{username} </if> <if test="password!=null"> and password=#{password} </if> <if test="age!=null"> and age=#{age} </if> <if test="phone!=null"> and phone=#{phone} </if> </trim> </select>
<where>標簽
這里是解決了 where 子句中開頭出現多余的 and,如果我們一個限制條件都不加入呢?
@Test void selectByCondition() { UserInfo userInfo = new UserInfo(); //userInfo.setUsername("彭于晏"); //userInfo.setPhone("34567"); log.info(userInfoMapper.selectByCondition(userInfo).toString()); }
這樣就會出現問題,那么這樣如何解決呢?MyBatis 為我們提供了 <where>
標簽用來解決查詢語句的 where 子句出現的各種問題,包括開頭出現的多余的 and 和 where 子句無內容的情況。
<select id="selectByCondition" resultType="com.example.mybatis20240101.model.UserInfo"> select * from userinfo <where> <if test="username!=null"> username=#{username} </if> <if test="password!=null"> and password=#{password} </if> <if test="age!=null"> and age=#{age} </if> <if test="phone!=null"> and phone=#{phone} </if> </where> </select>
可以看到,當我們 where 子句為空的時候,使用 <where>
標簽會自動刪除 where 子句,它也可以幫助我們刪除多余的 and,下面的報錯咱們不管,這時因為我們沒加 where 子句,所以就相當于查詢整個表,但是我們方法的返回值是 UserInfo,改為列表就可以了。
當 where 子句為空的時候,我們還有一種解決方式,就是自己加上一個 1=1 的條件,這樣當我們加的條件為空的時候就不會出現錯誤。
<select id="selectByCondition" resultType="com.example.mybatis20240101.model.UserInfo"> select * from userinfo where 1=1 <trim prefixOverrides="and"> <if test="username!=null"> username=#{username} </if> <if test="password!=null"> and password=#{password} </if> <if test="age!=null"> and age=#{age} </if> <if test="phone!=null"> and phone=#{phone} </if> </trim> </select>
修改操作
這個修改操作就是前面我們舉的一個例子,我們并不知道用戶會修改哪些信息,所以我們這里就需要使用到動態(tài) SQL 來解決這個問題。
void updateByCondition(UserInfo userInfo);
<update id="updateByCondition"> update userinfo set <if test="password!=null"> password=#{password}, </if> <if test="age!=null"> age=#{age}, </if> <if test="gender!=null"> gender=#{gender}, </if> <if test="phone!=null"> phone=#{phone} </if> where <if test="username!=null"> username=#{username} </if> </update>
@Test void updateByCondition() { UserInfo userInfo = new UserInfo(); userInfo.setUsername("小美"); userInfo.setPassword("666666"); userInfo.setPhone("23456"); userInfoMapper.updateByCondition(userInfo); }
<set>標簽
為了解決 set 中出現的 set 子句中多余的逗號的問題,可以使用 <set>
標簽。
<update id="updateByCondition"> update userinfo <set> <if test="password!=null"> password=#{password}, </if> <if test="age!=null"> age=#{age}, </if> <if test="gender!=null"> gender=#{gender}, </if> <if test="phone!=null"> phone=#{phone} </if> </set> <where> <if test="username!=null"> username=#{username} </if> </where> </update>
如果 set 子句為空的時候,是會報錯的,通過 <set>
標簽無法解決,我們應該避免用戶 set 輸入空的子句。
刪除操作 <foreach>標簽
當我們想要在刪除的時候刪除不定數量的條件時,delete from userinfo where name in("小美","小帥")
,因為條件的數量是不確定的,所以我們在定義的時候就不知道 where 子句后面有多少個,所以這里我們就需要用到 <foreach>
標簽。
<foreach>
標簽可以對集合進行遍歷,標簽具有以下屬性:
- collection:綁定?法參數中的集合,如 List,Set,Map或數組對象
- item:遍歷時的每?個對象
- open:語句塊開頭的字符串
- close:語句塊結束的字符串
- separator:每次遍歷之間間隔的字符串
void deleteByCondition(List<Integer> list);
<delete id="deleteByCondition"> delete from userinfo where id in <foreach collection="list" item="id" open="(" close=")"> #{id} </foreach> </delete>
<include>標簽
在xml映射?件中配置的SQL,有時可能會存在很多重復的?段,此時就會存在很多冗余的代碼
我們可以對重復的代碼?段進?抽取,將其通過 <sql>
標簽封裝到?個SQL?段,然后再通過<include>
標簽進?引?。
ArrayList<UserInfo> selectAll();
<sql id="allColumn"> id, username, age, gender, phone, delete_flag, create_time, update_time </sql>
<select id="selectAll" resultType="com.example.mybatis20240101.model.UserInfo"> select <include refid="allColumn"></include> from userinfo </select>
這樣就可以減少很多重復的代碼。
以上就是詳解MyBatis特性之動態(tài)SQL的詳細內容,更多關于MyBatis動態(tài)SQL的資料請關注腳本之家其它相關文章!
相關文章
聊聊spring boot的WebFluxTagsProvider的使用
這篇文章主要介紹了聊聊spring boot的WebFluxTagsProvider的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-07-07springboot jta atomikos實現分布式事物管理
這篇文章主要介紹了springboot jta atomikos實現分布式事物管理,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-12-12Spring Boot整合RabbitMQ開發(fā)實戰(zhàn)詳解
這篇文章主要介紹了Spring Boot整合RabbitMQ開發(fā)實戰(zhàn),小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-02-02maven install報錯中程序包xxx不存在的問題解決
本文主要介紹了maven install報錯中程序包xxx不存在的問題解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-05-05