欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談Mybatis傳參類型如何確定

 更新時(shí)間:2021年10月27日 14:15:22   作者:一灰灰  
最近有小伙伴在討論#{}與${}的區(qū)別時(shí),有提到#{}是用字符串進(jìn)行替換,本文主要介紹了mapper接口中不同的參數(shù)類型,最終拼接sql中是如何進(jìn)行替換的,感興趣的可以了解一下

最近有小伙伴在討論#{}與${}的區(qū)別時(shí),有提到#{}是用字符串進(jìn)行替換,就我個(gè)人的理解,它的主要作用是占位,最終替換的結(jié)果并不一定是字符串方式,比如我們傳參類型是整形時(shí),最終拼接的sql,傳參講道理也應(yīng)該是整形,而不是字符串的方式

接下來(lái)我們來(lái)看一下,mapper接口中不同的參數(shù)類型,最終拼接sql中是如何進(jìn)行替換的

I. 環(huán)境配置

我們使用SpringBoot + Mybatis + MySql來(lái)搭建實(shí)例demo

springboot: 2.2.0.RELEASE
mysql: 5.7.22

1. 項(xiàng)目配置

<dependencies>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

核心的依賴mybatis-spring-boot-starter,至于版本選擇,到mvn倉(cāng)庫(kù)中,找最新的
另外一個(gè)不可獲取的就是db配置信息,appliaction.yml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password:

2. 數(shù)據(jù)庫(kù)表

用于測(cè)試的數(shù)據(jù)庫(kù)

CREATE TABLE `money` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用戶名',
  `money` int(26) NOT NULL DEFAULT '0' COMMENT '錢(qián)',
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=551 DEFAULT CHARSET=utf8mb4;

測(cè)試數(shù)據(jù),主要是name字段,值為一個(gè)數(shù)字的字符串

INSERT INTO `money` (`id`, `name`, `money`, `is_deleted`, `create_at`, `update_at`)
VALUES
 (120, '120', 200, 0, '2021-05-24 20:04:39', '2021-09-27 19:21:40');

II. 傳參類型確定

本文忽略掉mybatis中的po、mapper接口、xml文件的詳情,有興趣的小伙伴可以直接查看最下面的源碼(或者查看之前的博文也可以)

1. 參數(shù)類型為整形

針對(duì)上面的case,定義一個(gè)根據(jù)name查詢數(shù)據(jù)的接口,但是這個(gè)name參數(shù)類型為整數(shù)
mapper接口:

/**
 * int類型,最終的sql中參數(shù)替換的也是int
 * @param name
 * @return
 */
List<MoneyPo> queryByName(@Param("name") Integer name);

對(duì)應(yīng)的xml文件如下

<select id="queryByName" resultMap="BaseResultMap">
    select * from money where `name` = #{name}
</select>

上面這個(gè)寫(xiě)法非常常見(jiàn)了,我們現(xiàn)在的問(wèn)題就是,傳參為整數(shù),那么最終的sql是 name = 120 還是 name = '120'呢?
那么怎么確定最終生成的sql是啥樣的呢?這里介紹一個(gè)直接輸出mysql執(zhí)行sql日志的方式
在mysql服務(wù)器上執(zhí)行下面兩個(gè)命令,開(kāi)啟sql執(zhí)行日志

set global general_log = "ON";
show variables like 'general_log%';

當(dāng)我們?cè)L問(wèn)上面的接口之后,會(huì)發(fā)現(xiàn)最終發(fā)送給mysql的sql語(yǔ)句中,參數(shù)替換之后依然是整數(shù)

select * from money where `name` = 120

2. 指定jdbcType

在使用#{}, ${}時(shí),有時(shí)也會(huì)看到除了參數(shù)之外,還會(huì)指定jdbcType,那么我們?cè)趚ml中指定這個(gè)對(duì)最終的sql生成會(huì)有影響么?

<select id="queryByNameV2" resultMap="BaseResultMap">
    select * from money where `name` = #{name, jdbcType=VARCHAR} and 0=0
</select>

生成的sql如下

select * from money where `name` = 120 and 0=0

從實(shí)際的sql來(lái)看,這個(gè)jdbcType并沒(méi)有影響最終的sql參數(shù)拼接,那它主要是干嘛用呢?(它主要適用于傳入null時(shí),類型轉(zhuǎn)換可能出現(xiàn)的異常)

3. 傳參類型為String

當(dāng)我們傳參類型為string時(shí),最終的sql講道理應(yīng)該會(huì)帶上引號(hào)

/**
 * 如果傳入的參數(shù)類型為string,會(huì)自動(dòng)帶上''
 * @param name
 * @return
 */
List<MoneyPo> queryByNameV3(@Param("name") String name);

對(duì)應(yīng)的xml

<select id="queryByNameV3" resultMap="BaseResultMap">
    select * from money where `name` = #{name, jdbcType=VARCHAR} and 1=1
</select>

上面這個(gè)最終生成的sql如下
select * from money where `name` = '120' and 1=1

4. TypeHandler實(shí)現(xiàn)參數(shù)替換強(qiáng)制添加引號(hào)

看完上面幾節(jié),基本上可以有一個(gè)得出一個(gè)簡(jiǎn)單的推論(當(dāng)然對(duì)不對(duì)則需要從源碼上分析了)

sql參數(shù)替換,最終并不是簡(jiǎn)單使用字符串來(lái)替換,實(shí)際上是由參數(shù)java的參數(shù)類型決定,若java參數(shù)類型為字符串,拼接的sql為字符串格式;傳參為整型,拼接的sql也是整數(shù)

那么問(wèn)題來(lái)了,為什么要了解這個(gè)?

關(guān)鍵點(diǎn)在于索引失效的問(wèn)題

比如本文實(shí)例中的name上添加了索引,當(dāng)我們的sql是 select * from money where name = 120 會(huì)走不了索引,如果想走索引,要求傳入的參數(shù)必須是字符串,不能出現(xiàn)隱式的類型轉(zhuǎn)換

基于此,我們就有一個(gè)應(yīng)用場(chǎng)景了,為了避免由于傳參類型問(wèn)題,導(dǎo)致走不了索引,我們希望name的傳參,不管實(shí)際傳入?yún)?shù)類型是什么,最終拼接的sql,都是字符串的格式;

我們借助自定義的TypeHandler來(lái)實(shí)現(xiàn)這個(gè)場(chǎng)景

@MappedTypes(value = {Long.class, Integer.class})
@MappedJdbcTypes(value = {JdbcType.CHAR, JdbcType.VARCHAR, JdbcType.LONGVARCHAR})
public class StrTypeHandler extends BaseTypeHandler<Object> {

    /**
     * java 類型轉(zhuǎn) jdbc類型
     *
     * @param ps
     * @param i
     * @param parameter
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, String.valueOf(parameter));
    }

    /**
     * jdbc類型轉(zhuǎn)java類型
     *
     * @param rs
     * @param columnName
     * @return
     * @throws SQLException
     */
    @Override
    public Object getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getString(columnName);
    }

    @Override
    public Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getString(columnIndex);
    }

    @Override
    public Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getString(columnIndex);
    }
}

然后在xml中,指定TypeHandler

/**
 * 通過(guò)自定義的 TypeHandler 來(lái)實(shí)現(xiàn) java <-> jdbc 類型的互轉(zhuǎn),從而實(shí)現(xiàn)即時(shí)傳入的是int/long,也會(huì)轉(zhuǎn)成String
 * @param name
 * @return
 */
List<MoneyPo> queryByNameV4(@Param("name") Integer name);
<select id="queryByNameV4" resultMap="BaseResultMap">
    select * from money where `name` = #{name, jdbcType=VARCHAR, typeHandler=com.git.hui.boot.mybatis.handler.StrTypeHandler} and 2=2
</select>

上面這種寫(xiě)法輸出的sql就會(huì)攜帶上單引號(hào),這樣就可以從源頭上解決傳參類型不對(duì),導(dǎo)致最終走不了索引的問(wèn)題

select * from money where `name` = '120' and 2=2

5. 小結(jié)

本文通過(guò)一個(gè)簡(jiǎn)單的實(shí)例,來(lái)測(cè)試Mapper接口中,不同的參數(shù)類型,對(duì)最終的sql生成的影響

參數(shù)類型為整數(shù)時(shí),最終的sql的參數(shù)替換也是整數(shù)(#{}并不是簡(jiǎn)單的字符串替換哦)
參數(shù)類型為字符串時(shí),最終的sql參數(shù)替換,會(huì)自動(dòng)攜帶'' (${}注意它不會(huì)自動(dòng)帶上單引號(hào),需要自己手動(dòng)添加)

當(dāng)我們希望不管傳參什么類型,最終生成的sql,都是字符串替換時(shí),可以借助自定義的TypeHandler來(lái)實(shí)現(xiàn),這樣可以從源頭上避免因?yàn)殡[式類型轉(zhuǎn)換導(dǎo)致走不了索引問(wèn)題
最后疑問(wèn)來(lái)了,上面的結(jié)論靠譜么?mybatis中最終的sql是在什么地方拼接的?這個(gè)sql拼接的流程是怎樣的呢?
關(guān)于sql的拼接全流程,后續(xù)博文即將上線,我是一灰灰,走過(guò)路過(guò)的各位大佬幫忙點(diǎn)個(gè)贊、價(jià)格收藏、給個(gè)評(píng)價(jià)唄

 到此這篇關(guān)于Mybatis傳參類型如何確定的文章就介紹到這了,更多相關(guān)Mybatis傳參類型如何確定內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論