Mybatis關(guān)于動(dòng)態(tài)排序 #{} ${}問(wèn)題
Mybatis動(dòng)態(tài)排序 #{} ${}問(wèn)題
在寫(xiě)Mybatis動(dòng)態(tài)排序是遇到一個(gè)問(wèn)題,開(kāi)始,我是這樣寫(xiě)的
<if test="orderField !=null and orderField != '' ">
? ? order by t.#{orderField} ?#{orderType}
</if>發(fā)現(xiàn)報(bào)錯(cuò),后來(lái)經(jīng)過(guò)查閱資料發(fā)現(xiàn),用#{}會(huì)多個(gè)' '導(dǎo)致SQL語(yǔ)句失效。
就是說(shuō),向上面這樣的,連續(xù)使用#{}進(jìn)行注入的,會(huì)導(dǎo)致SQL語(yǔ)句失效。
所以,改成${}注入就可以了
<if test="orderField !=null and orderField != '' ">
? ? order by t.${orderField} ?${orderType}
</if>通過(guò)動(dòng)態(tài)排序理解#{}和${}的區(qū)別
在日常開(kāi)發(fā)中,尤其是在數(shù)據(jù)列表展示中,排序是最基本的功能。一般根據(jù)創(chuàng)建時(shí)間倒敘,但有可能碰到動(dòng)態(tài)排序的需求。
接下來(lái),我們將圍繞由后臺(tái)動(dòng)態(tài)排序進(jìn)行探討
例如
現(xiàn)在,我們要查詢一張店長(zhǎng)表tb_director,我們?cè)谠械母割愔?,新定義兩個(gè)字段
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
?* Entity基類
?*
?* @author 進(jìn)擊的Java君
?*/
public class BaseEntity implements Serializable
{
? ? private static final long serialVersionUID = 1L;
? ? /** 排序列*/
? ? private String orderField;
? ? /** 排序規(guī)則,升降序*/
? ? private String orderType;
? ? /** 搜索值 */
? ? private String searchValue;
? ? /** 創(chuàng)建者 */
? ? private String createBy;
? ? /** 創(chuàng)建時(shí)間 */
? ? @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
? ? private Date createTime;
? ? /** 更新者 */
? ? private String updateBy;
? ? /** 更新時(shí)間 */
? ? @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
? ? private Date updateTime;
? ? /** 備注 */
? ? private String remark;
? ? /** 開(kāi)始時(shí)間 */
? ? @JsonIgnore
? ? private String beginTime;
? ? /** 結(jié)束時(shí)間 */
? ? @JsonIgnore
? ? private String endTime;
? ? /** 請(qǐng)求參數(shù) */
? ? private Map<String, Object> params;
}
/**
?* 店長(zhǎng)表
?* @author 進(jìn)擊的Java君
?* @date 2021-03-18
?*/
@Entity
@Getter
@Setter
@Table(name = "tb_director")
public class Director extends BaseEntity {
? ??
? ? ?/** 主鍵id */
? ? @Id
? ? @GeneratedValue(strategy = GenerationType.IDENTITY)
? ? private Long id;
? ?
? ? ?/** 店鋪名稱 */
? ? @Column(name = "director_name", unique = true)
? ? private String directorName;
? ? /** 店鋪地址 */
? ? @Column(name = "director_adress", unique = true)
? ? private String directorAdress;
}現(xiàn)在,我們只需要在mapper.xml中加上sql過(guò)濾條件即可
? <!-- 查詢店長(zhǎng)信息 -->
? <sql id="selectDirectorVo">
? ? ?select id, director_name,director_adress,director_num,director_create_time,director_up_time,openId
? ? ? ? ?from tb_director
? </sql>
? <!-- 查詢條件 -->
? <sql id="sqlwhereSearch">
? ? ? ? ?<where>
? ? ? ? ? ? ?<if test="directorName !=null and directorName !=''">
? ? ? ? ? ? ? ? ? ? ? AND director_name like concat('%', #{directorName}, '%')
? ? ? ? ? ? ?</if>
? ? ? ? ? ? ?<if test="openId !=null and openId !=''">
? ? ? ? ? ? ? ? ? ? ? AND openId=#{openId}
? ? ? ? ? ? </if>
? ? ? ? ? ? ? ? <if test="id !=null and id !=''">
? ? ? ? ? ? ? ? ? ? ? AND id=#{id}
?? ??? ??? ?</if>
? ? ?? ??? ?<if test="beginTime != null and beginTime != ''"><!-- 開(kāi)始時(shí)間檢索 -->
? ? ? ? ?? ??? ??? ? ?AND date_format(directorCreateTime,'%y%m%d') >= date_format(#{beginTime},'%y%m%d')
? ? ? ? ?? ?</if>
? ? ? ? ? ? <if test="endTime != null and endTime != ''"><!-- 結(jié)束時(shí)間檢索 -->
? ? ? ? ? ? ? ? ?? ? ?AND date_format(directorCreateTime,'%y%m%d') <= date_format(#{endTime},'%y%m%d')
? ? ? ? ? ? </if>
? ? ? ? ? ? </where>
? ? ? ? ? ? <!-- 根據(jù)傳入字段動(dòng)態(tài)過(guò)濾 -->
? ? ? ? ? ? <if test="orderField !=null and orderField != '' ">
? ? ? ? ? ? ? ? ? ? ? ? order by ${orderField} ?${orderType}
?? ??? ??? ?</if>
? ? ? ? </sql>
? <!-- 根據(jù)條件查詢店長(zhǎng) -->
? <select id="sel" parameterType="Director" resultMap="DirectorResult">
? ? ? ? ? ? ? ? <include refid="selectDirectorVo"/>
? ? ? ? ? ? ? ? <include refid="sqlwhereSearch"/>
? ? ? ? </select>持久層代碼編完后,我們只需要在調(diào)用時(shí),傳入我們想進(jìn)行排序的字段即可。
如下所示:
127.0.0.1:8080/api/director/sel?orderField=director_create_time&orderType=desc
但是這樣的話,就需要我們對(duì)表中的字段非常清楚,如果覺(jué)得這樣不舒服的話,我們可以對(duì)sql進(jìn)行修改
<if test="orderField !=null and orderField != '' ">
?? ?order by
?? ?<choose>
?? ??? ?<when test="orderField == 'directorName'">
?? ??? ??? ?director_name ${orderType}
?? ??? ?</when>
?? ??? ?<when test="orderField == 'openId'">
?? ??? ??? ?openId ${orderType}
?? ??? ?</when>
?? ??? ?<otherwise>
?? ??? ??? ?create_time ${orderType}
?? ??? ?</otherwise>
?? ?</choose>
</if>注意事項(xiàng)
使用這樣連續(xù)拼接兩個(gè)注入?yún)?shù)時(shí),只能用${},不能用#{}。
如果使用#{orderField},則會(huì)被解析成ORDER BY “orderField”,這顯然是一種錯(cuò)誤的寫(xiě)法。
- $ 符號(hào)一般用來(lái)當(dāng)作占位符
 - #{}是sql的參數(shù)占位符,Mybatis會(huì)將sql中的#{}替換為?號(hào),在sql執(zhí)行前會(huì)使用PreparedStatement的參數(shù)設(shè)置方法,按序給sql的?號(hào)占位符設(shè)置參數(shù)值。
 
預(yù)編譯的機(jī)制。預(yù)編譯是提前對(duì)SQL語(yǔ)句進(jìn)行預(yù)編譯,而其后注入的參數(shù)將不會(huì)再進(jìn)行SQL編譯。我們知道,SQL注入是發(fā)生在編譯的過(guò)程中,因?yàn)閻阂庾⑷肓四承┨厥庾址?,最后被編譯成了惡意的執(zhí)行操作。而預(yù)編譯機(jī)制則可以很好的防止SQL注入。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
- MyBatis中#{}?和?${}?的區(qū)別和動(dòng)態(tài)?SQL詳解
 - mybatis中${}和#{}的區(qū)別以及底層原理分析
 - MyBatis #{}和${} |與數(shù)據(jù)庫(kù)連接池使用詳解
 - MyBatis中使用#{}和${}占位符傳遞參數(shù)的各種報(bào)錯(cuò)信息處理方案
 - mybatis中#{}和${}的區(qū)別詳解
 - MyBatis中#{}和${}有哪些區(qū)別
 - mybatis中${}和#{}取值的區(qū)別分析
 - MyBatis中#{}占位符與${}拼接符的用法說(shuō)明
 - 詳解Mybatis中的 ${} 和 #{}區(qū)別與用法
 - Mybatis之#{}與${}的區(qū)別使用詳解
 - Mybatis中#{}與${}的區(qū)別詳解
 - MyBatis中 #{} 和 ${} 的區(qū)別小結(jié)
 
相關(guān)文章
 自定義一個(gè)異常類模板的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇自定義一個(gè)異常類模板的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-10-10
 Java源碼解析之Gateway請(qǐng)求轉(zhuǎn)發(fā)
今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Gateway請(qǐng)求轉(zhuǎn)發(fā)展開(kāi),文中有非常詳細(xì)介紹及代碼示例,需要的朋友可以參考下2021-06-06
 關(guān)于Socket的解析以及雙方即時(shí)通訊的java實(shí)現(xiàn)方法
本篇文章主要介紹了關(guān)于Socket的解析以及雙方通訊的java實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
 JAVA實(shí)現(xiàn)第三方短信發(fā)送過(guò)程詳解
這篇文章主要介紹了JAVA實(shí)現(xiàn)第三方短信發(fā)送過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
 Java?中?hashCode()?與?equals()?的關(guān)系(面試)
這篇文章主要介紹了Java中hashCode()與equals()的關(guān)系,ava中hashCode()和equals()的關(guān)系是面試中的??键c(diǎn),文章對(duì)hashCode與equals的關(guān)系做出詳解,需要的小伙伴可以參考一下2022-09-09
 關(guān)于IDEA創(chuàng)建spark maven項(xiàng)目并連接遠(yuǎn)程spark集群?jiǎn)栴}
這篇文章主要介紹了IDEA創(chuàng)建spark maven項(xiàng)目并連接遠(yuǎn)程spark集群,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08
 使用Java對(duì)數(shù)據(jù)庫(kù)進(jìn)行基本的查詢和更新操作
這篇文章主要介紹了使用Java對(duì)數(shù)據(jù)庫(kù)進(jìn)行基本的查詢和更新操作,是Java入門(mén)學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-10-10

