Mybatis動態對映,這次終於搞明白了
動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記新增必要的空格,還要注意去掉列表最後一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。
使用動態 SQL 並非一件易事,但藉助可用於任何 SQL 對映語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。
如果你之前用過 JSTL 或任何基於類 XML 語言的文字處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間瞭解大量的元素。藉助功能強大的基於 OGNL 的表示式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。
Mybatis動態對映,這次終於搞明白了
choose(when,otherwise):相當於java中的switch語句,通常與when和otherwise搭配。
set:解決動態更新語句。
trim:靈活的去除多餘的關鍵字。
foreach:迭代一個集合,通常用於in條件。
實際工作中很多時候,這幾個標籤都是組合著使用。
今天的演示使用的是 Spring-Boot+Mybatis 進行演示,對於Spring-Boot整合Mybatis推薦:
if+where實現多條件查詢
建立一種昂資料庫表:
CREATE TABLE `m_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`gender` int(11) DEFAULT NULL COMMENT '0:女生 1:男生',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;
初始化幾條資料:
Mybatis動態對映,這次終於搞明白了
先來看UserMapper.xml檔案:
<mapper namespace="com.tian.mybatis.mapper.UserMapper">
<resultMap id="User" type="com.tian.mybatis.entity.User">
<id column="id" property="id"/>
<result column="name" property="userName"/>
</resultMap>
<select id="selectUserById" resultMap="User">
select * from m_user
<where>
<if test="id != null">
id = #{id}
</if>
<if test="name != null and name != ''">
and `name` = #{name}
</if>
</where>
</select>
</mapper>
UserMapper.java內容:
import com.tian.mybatis.entity.User;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper {
User selectUserById(@Param("name") String userName, @Param("id") Integer id);
}
UserService.java內容:
public interface UserService {
User selectUserById(String userName, Integer id);
}
UserServiceImpl.java內容:
import com.tian.mybatis.entity.User;
import com.tian.mybatis.mapper.UserMapper;
import com.tian.mybatis.service.UserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public User selectUserById(String userName, Integer id) {
return userMapper.selectUserById(userName, id);
}
}
UserController.java內容:
import com.tian.mybatis.entity.User;
import com.tian.mybatis.service.UserService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class UserController {
@Resource
private UserService userService;
@GetMapping("/test")
public User selectUserById() {
return userService.selectUserById("tian", 1);
}
}
Application.java內容:
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.tian.mybatis.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
把專案啟動起來,然後進行訪問/test。
http://localhost :9002/test
返回:
Mybatis動態對映,這次終於搞明白了
上面的這個案例也是我們工作中的程式碼案例,我們工作但部分都使用這種方式。
Mybatis動態對映,這次終於搞明白了
下面的所有演示都是基於上面這些程式碼進行調整而成的。
回到正題。
上面的案例中使用了where+if。案例中貌似有個問題:
Mybatis動態對映,這次終於搞明白了
如果id=null,豈不是多了個and嗎?
我們修改controller中的程式碼
@GetMapping("/test")
public User selectUserById() {
return userService.selectUserById("tian", null);
}
為了能讓sql輸出,我們在配置檔案新增了一個配置項:
logging:
level:
com:
tian:
mybatis:
mapper: debug
再次執行,輸出和前面是一樣的。控制檯輸出的sql中並沒有and。這就是所謂的動態對映的強大功能之一。
如果我們不使用動態對映標籤,在處理or或者and的時候很有可能出問題。
if元素的test用於判斷表示式是否符合,符合則繼續拼接SQL語句。
建議
建議使用這種動態標籤,不要使用原生態,因為有時候總有意想不到的判斷導致多了一個and或者or,於是就出現bug,嚴重的可能導致線上某個功能不可能用。
if+trim+foreach實現多條件查詢
對前面的程式碼進行調整
<select id="selectUsersByIds" resultMap="User">
select * from m_user
<trim prefix="where" prefixOverrides="and | or">
<if test="idList != null">
id in
<foreach collection="idList" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</if>
<if test="gender != null and gender != 0">
AND gender = #{gender}
</if>
</trim>
</select>
UserMapper.java增加
List<User> selectUsersByIds(@Param("idList") List<Integer> idList, @Param("gender") Integer gender);
controller新增方法:
@GetMapping("/users")
public List<User> selectUsersByIds() {
List<Integer> idList = new ArrayList<>();
idList.add(1);
idList.add(2);
idList.add(3);
idList.add(4);
idList.add(5);
return userService.selectUsersByIds(idList, null);
}
專案跑起來,訪問
http://localhost :9002/users
輸出:
Mybatis動態對映,這次終於搞明白了
sql輸出:
對上面相關屬性進行說明
trim的屬性
prefix:字首: 作用是通過自動識別是否有返回值後,在trim包含的內容上加上字首,如上述示例的where。
suffix:字尾: 作用是在trim包含的內容上加上字尾。
prefixOverrides::對於trim包含內容的首部進行指定內容,(如上述示例的 and | or) 的忽略(去餘);
suffixOverrides::對於trim包含內容的首位部進行指定內容的忽略。
foreach的屬性
item:表示集合中每一個元素進行迭代時的別名。
index::指定一個名稱,表示在迭代的過程中,每次迭代到的位置。
open:表示該語句以什麼開始(既然是in條件語句,必然是 ' ( ' 開始)
separator::表示每次進行迭代的時候以什麼符號作為分隔符(既然是in條件語句,必然是 ' , ' 分隔)
close::表示該語句以什麼結束(既然是in條件語句,必然是 ' ) ' 結束)
collection:最關鍵,並且最容易出錯的屬性。需注意,該屬性必須指定,不同情況下,該屬性值是不同的,主要有三種情況:
Mybatis動態對映,這次終於搞明白了
@Param是Mybatis中的註解,寫的時候別引用錯了,@Param("name"),這裡的name就是我們在Mapper.xml中使用的名稱。
在專案中我見過很多人這麼幹,就是當where語句後面不太確定能有條件出現時,使用
slect ...from...where 1=1
看看你的程式碼是否也有?
set
set元素可以用於動態包含需要更新的列,忽略其它不更新的列。
UserMapper.xml新增
<update id="updateAuthorIfNecessary">
update m_user
<set>
<if test="userName != null and userName != ''">
`name` = #{userName},
</if>
<if test="gender != null and gender != 0">
gender = #{gender},
</if>
<if test="age != null and age != 0">
age = #{age},
</if>
</set>
where id=#{id}
</update>
UserMapper.java新增
int updateAuthorIfNecessary(User user);
controller新增
@PostMapping("/updateUser")
public String update() {
User user = new User();
user.setAge(18);
user.setUserName("田哥");
user.setId(1);
return userService.updateAuthorIfNecessary(user) == 1 ? "success" : "fail";
}
重啟專案,訪問
http://localhost :9002/updateUser
輸出:success
資料庫表中資料已經修改成功:
SQL輸出
這個例子中,set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。
換一種方式
<trim prefix="SET" suffixOverrides=",">
...
</trim>
我們把上面的xml中diam進行調整:
<update id="updateAuthorIfNecessary">
update m_user
<trim prefix="SET" suffixOverrides=",">
<if test="userName != null and userName != ''">
`name` = #{userName},
</if>
<if test="gender != null and gender != 0">
gender = #{gender},
</if>
<if test="age != null and age != 0">
age = #{age},
</if>
</trim>
where id=#{id}
</update>
controller修改:
@PostMapping("/updateUser")
public String update() {
User user = new User();
user.setAge(19);
user.setUserName("tian");
user.setId(1);
return userService.updateAuthorIfNecessary(user) == 1 ? "success" : "fail";
}
最後看看SQL輸出:
Mybatis動態對映,這次終於搞明白了
自動給我們加上了SET關鍵字。並且資料庫修改成功。
Mybatis動態對映,這次終於搞明白了
choose
相當於Java中的switch語句,通常與when和otherwise搭配。
有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
下面我們繼續使用上面的案例程式碼進行演示。
UserMapper.xml新增方法:
<select id="selectUsersByName" resultMap="User">
select * from m_user where age = 19
<choose>
<when test="userName != null and userName != ''">
and `name` = #{userName}
</when>
<otherwise>
AND gender = 1
</otherwise>
</choose>
</select>
controller新增方法:
@GetMapping("/user/name")
public List<User> selectUsersByName() {
return userService.selectUsersByName("tian");
}
返回:
SQL輸出:
Mybatis動態對映,這次終於搞明白了
正確的輸出。如果我們userName沒有是null呢?
輸出和上面正常,在看看SQL輸出:
Mybatis動態對映,這次終於搞明白了
因為我們的userName的條件不滿足的情況下,直接執行了gender。
上面 <otherwise> 就類似於相當於我們java語法中switch中的default,當前麵條件不滿足的時候,執行default模組一樣。
Bind
這種方式使用的不是很多,但是也是有用的。 bind 元素允許你在 OGNL 表示式以外建立一個變數,並將其繫結到當前的上下文。比如:
<select id="selectUserByName" resultType="User">
<bind name="pattern" value="'%' + userName + '%'" />
select * from m_user
WHERE `name` LIKE #{pattern}
</select>
還有就是script,這個就沒有必要在演示了,在工作中基本上用不上。它就是把SQL解除安裝Java程式碼中。比如
@Update({"<script>",
"update m_user",
" <set>",
" <if test='username != null'>`name`=#{username},</if>",
" <if test='gender != null and gender != 0'>gender=#{gender},</if>",
" </set>",
"where id=#{id}",
"</script>"})
void updateUserValues(User user);
總結
文章中部分知識為了演示,可能有些程式碼不是很規範,尤其是sql部分,我們在開發中,針對使用Mybatis開發,我個人總結了幾個點:
表中是否已經有索引,有索引的時候我們的SQL中是否有用上。
返回欄位儘量不要寫星號*,建議寫成需要的欄位。
關鍵字建議都寫成大寫,更好的區別非關鍵字。
遇到表中欄位和資料庫關鍵一樣的時候,記得單引號。
使用@Param註解注意一定要使用Mybatis中的註解。
使用不管是一個引數還是多個引數時,使用註解@Param指定名稱,方便日後需要再次新增欄位。
強烈建議使用動態標籤,避免出現多出and或者or關鍵字的SQL錯誤,同時也不用再寫where 1=1
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69989885/viewspace-2741632/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 這一次,Google 終於對 Web 自動化下手了!GoWeb
- MyBatis框架之SQL對映和動態SQLMyBatis框架SQL
- Java 21 終於對這些功能動刀了!!Java
- YUI TreeView搞明白了UIView
- 這一次,徹底幫你搞明白 ImageView ScaleTypeView
- MyBatis(四) 對映器配置(自動對映、resultMap手動對映、引數傳遞)MyBatis
- 終於明白了快三倍投必死
- 終於弄明白了 RocketMQ 的儲存模型MQ模型
- 程式設計師要搞明白CDN,這篇應該夠了程式設計師
- Mybatis結果對映MyBatis
- elasticsearch的字串動態對映Elasticsearch字串
- XPage系列|這次升級後終於是全自動化註冊了!
- 終於有人把網路爬蟲講明白了爬蟲
- 終於有人把隱私計算講明白了
- 一直讓 PHP 程式設計師懵逼的同步阻塞非同步非阻塞,終於搞明白了PHP程式設計師非同步
- EntityFramework Core如何對映動態模型?Framework模型
- Mybatis 基礎xml對映MyBatisXML
- 終於有人把Web 3.0和元宇宙講明白了Web元宇宙
- ClickHouse與Hive的區別,終於有人講明白了Hive
- MyBatis對動態SQL的支援MyBatisSQL
- 分析1000+問卷後,我終於搞明白宅家的人都在想什麼
- MyBatis從入門到精通(十一):MyBatis高階結果對映之一對多對映MyBatis
- MyBatis從入門到精通(九):MyBatis高階結果對映之一對一對映MyBatis
- Docker 如何動態修改容器埠對映Docker
- MyBatis 動態 SQL 最全教程,這樣寫 SQL 太優雅了!MyBatisSQL
- 花了一個星期,我終於把RPC框架整明白了!RPC框架
- 終於有人把工業資料採集講明白了
- 終於有人把能把資料採集給講明白了
- Mybatis實體關聯對映MyBatis
- mybatis關聯關係對映MyBatis
- Mybatis對映檔案簡介MyBatis
- mybatis高階結果對映MyBatis
- MyBatis 結果對映總結MyBatis
- teprunner測試平臺定時任務這次終於穩了
- 搞明白這四個問題Linux就好學多了Linux
- Mybatis 裡對映檔案的動態 SQL 語句,實現if,where,foreache的SQL語句動態拼接查詢MyBatisSQL
- Mybatis入門篇之結果對映,你射準了嗎?MyBatis
- 關於LLM-as-a-judge正規化,終於有綜述講明白了