Mybatis動態映射,原來如此簡單


動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。

使用動態 SQL 並非一件易事,但借助可用於任何 SQL 映射語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。

如果你之前用過 JSTL 或任何基於類 XML 語言的文本處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間了解大量的元素。借助功能強大的基於 OGNL 的表達式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。

if:利用if實現簡單的條件選擇。
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;
初始化幾條數據:

先來看UserMapper.xml文件:

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

返回:

上面的這個案例也是我們工作中的代碼案例,我們工作但部分都使用這種方式。

下面的所有演示都是基於上面這些代碼進行調整而成的。

回到正題。

上面的案例中使用了where+if。案例中貌似有個問題:

如果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的時候很有可能出問題。

where元素可以智能的處理and 和 or 的多余問題, 不需擔心多余關鍵字導致語法錯誤。
if元素的test用於判斷表達式是否符合,符合則繼續拼接SQL語句。
建議

建議使用這種動態標簽,不要使用原生態,因為有時候總有意想不到的判斷導致多了一個and或者or,於是就出現bug,嚴重的可能導致線上某個功能不可能用。
if+trim+foreach實現多條件查詢
對前面的代碼進行調整

UserMapper.java增加

List selectUsersByIds(@Param("idList") List 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

輸出:

sql輸出:

對上面相關屬性進行說明

trim的屬性

prefix:前綴: 作用是通過自動識別是否有返回值后,在trim包含的內容上加上前綴,如上述示例的where。
suffix:后綴: 作用是在trim包含的內容上加上后綴。
prefixOverrides::對於trim包含內容的首部進行指定內容,(如上述示例的 and | or) 的忽略(去余);
suffixOverrides::對於trim包含內容的首位部進行指定內容的忽略。
foreach的屬性

item:表示集合中每一個元素進行迭代時的別名。
index::指定一個名稱,表示在迭代的過程中,每次迭代到的位置。
open:表示該語句以什么開始(既然是in條件語句,必然是 ' ( ' 開始)
separator::表示每次進行迭代的時候以什么符號作為分隔符(既然是in條件語句,必然是 ' , ' 分隔)
close::表示該語句以什么結束(既然是in條件語句,必然是 ' ) ' 結束)
collection:最關鍵,並且最容易出錯的屬性。需注意,該屬性必須指定,不同情況下,該屬性值是不同的,
主要有三種情況:

@Param是Mybatis中的注解,寫的時候別引用錯了,@Param("name"),這里的name就是我們在Mapper.xml中使用的名稱。

在項目中我見過很多人這么干,就是當where語句后面不太確定能有條件出現時,使用

slect ...from...where 1=1
看看你的代碼是否也有?

set
set元素可以用於動態包含需要更新的列,忽略其它不更新的列。

UserMapper.xml新增

update m_user `name` = #{userName}, gender = #{gender}, age = #{age}, where id=#{id} 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 關鍵字,並會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。

換一種方式

... 我們把上面的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輸出:

自動給我們加上了SET關鍵字。並且數據庫修改成功。

choose
相當於Java中的switch語句,通常與when和otherwise搭配。

有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。

下面我們繼續使用上面的案例代碼進行演示。

UserMapper.xml新增方法:

controller新增方法:

@GetMapping("/user/name")
public List selectUsersByName() {
return userService.selectUsersByName("tian");
}
返回:

SQL輸出:

正確的輸出。如果我們userName沒有是null呢?

輸出和上面正常,在看看SQL輸出:

因為我們的userName的條件不滿足的情況下,直接執行了gender。

上面 就類似於相當於我們java語法中switch中的default,當前面條件不滿足的時候,執行default模塊一樣。

Bind
這種方式使用的不是很多,但是也是有用的。bind 元素允許你在 OGNL 表達式以外創建一個變量,並將其綁定到當前的上下文。比如:

還有就是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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM