国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

mybatis深入理解(一)之 # 與 $ 區(qū)別以及 sql 預(yù)編譯

shadowbook / 1739人閱讀

摘要:在動(dòng)態(tài)解析階段,和會(huì)有不同的表現(xiàn)解析為一個(gè)預(yù)編譯語句的參數(shù)標(biāo)記符。其次,在預(yù)編譯之前已經(jīng)被變量替換了,這會(huì)存在注入問題。預(yù)編譯語句對(duì)象可以重復(fù)利用。默認(rèn)情況下,將對(duì)所有的進(jìn)行預(yù)編譯。總結(jié)本文主要深入探究了對(duì)和的不同處理方式,并了解了預(yù)編譯。

mybatis 中使用 sqlMap 進(jìn)行 sql 查詢時(shí),經(jīng)常需要?jiǎng)討B(tài)傳遞參數(shù),例如我們需要根據(jù)用戶的姓名來篩選用戶時(shí),sql 如下:

select * from user where name = "ruhua";

上述 sql 中,我們希望 name 后的參數(shù) "ruhua" 是動(dòng)態(tài)可變的,即不同的時(shí)刻根據(jù)不同的姓名來查詢用戶。在 sqlMap 的 xml 文件中使用如下的 sql 可以實(shí)現(xiàn)動(dòng)態(tài)傳遞參數(shù) name:

select * from user where name = #{name};

或者

select * from user where name = "${name}";

對(duì)于上述這種查詢情況來說,使用 #{ } 和 ${ } 的結(jié)果是相同的,但是在某些情況下,我們只能使用二者其一。

"#" 與 "$" 區(qū)別

動(dòng)態(tài) SQL 是 mybatis 的強(qiáng)大特性之一,也是它優(yōu)于其他 ORM 框架的一個(gè)重要原因。mybatis 在對(duì) sql 語句進(jìn)行預(yù)編譯之前,會(huì)對(duì) sql 進(jìn)行動(dòng)態(tài)解析,解析為一個(gè) BoundSql 對(duì)象,也是在此處對(duì)動(dòng)態(tài) SQL 進(jìn)行處理的。

在動(dòng)態(tài) SQL 解析階段, #{ } 和 ${ } 會(huì)有不同的表現(xiàn):

#{ } 解析為一個(gè) JDBC 預(yù)編譯語句(prepared statement)的參數(shù)標(biāo)記符。

例如,sqlMap 中如下的 sql 語句

select * from user where name = #{name};

解析為:

select * from user where name = ?;

一個(gè) #{ } 被解析為一個(gè)參數(shù)占位符 ?

而,

${ } 僅僅為一個(gè)純碎的 string 替換,在動(dòng)態(tài) SQL 解析階段將會(huì)進(jìn)行變量替換

例如,sqlMap 中如下的 sql

select * from user where name = "${name}";

當(dāng)我們傳遞的參數(shù)為 "ruhua" 時(shí),上述 sql 的解析為:

select * from user where name = "ruhua";

預(yù)編譯之前的 SQL 語句已經(jīng)不包含變量 name 了。

綜上所得, ${ } 的變量的替換階段是在動(dòng)態(tài) SQL 解析階段,而 #{ }的變量的替換是在 DBMS 中。

用法 tips

1、能使用 #{ } 的地方就用 #{ }

首先這是為了性能考慮的,相同的預(yù)編譯 sql 可以重復(fù)利用。

其次,${ } 在預(yù)編譯之前已經(jīng)被變量替換了,這會(huì)存在 sql 注入問題。例如,如下的 sql,

select * from ${tableName} where name = #{name} 

假如,我們的參數(shù) tableName 為 user; delete user; --,那么 SQL 動(dòng)態(tài)解析階段之后,預(yù)編譯之前的 sql 將變?yōu)?/p>

select * from user; delete user; -- where name = ?;

-- 之后的語句將作為注釋,不起作用,因此本來的一條查詢語句偷偷的包含了一個(gè)刪除表數(shù)據(jù)的 SQL!

2、表名作為變量時(shí),必須使用 ${ }

這是因?yàn)椋砻亲址褂?sql 占位符替換字符串時(shí)會(huì)帶上單引號(hào) "",這會(huì)導(dǎo)致 sql 語法錯(cuò)誤,例如:

select * from #{tableName} where name = #{name};

預(yù)編譯之后的sql 變?yōu)椋?/p>

select * from ? where name = ?;

假設(shè)我們傳入的參數(shù)為 tableName = "user" , name = "ruhua",那么在占位符進(jìn)行變量替換后,sql 語句變?yōu)?/p>

select * from "user" where name="ruhua";

上述 sql 語句是存在語法錯(cuò)誤的,表名不能加單引號(hào) ""(注意,反引號(hào) ``是可以的)。

sql預(yù)編譯 定義

sql 預(yù)編譯指的是數(shù)據(jù)庫驅(qū)動(dòng)在發(fā)送 sql 語句和參數(shù)給 DBMS 之前對(duì) sql 語句進(jìn)行編譯,這樣 DBMS 執(zhí)行 sql 時(shí),就不需要重新編譯。

為什么需要預(yù)編譯

JDBC 中使用對(duì)象 PreparedStatement 來抽象預(yù)編譯語句,使用預(yù)編譯

預(yù)編譯階段可以優(yōu)化 sql 的執(zhí)行
預(yù)編譯之后的 sql 多數(shù)情況下可以直接執(zhí)行,DBMS 不需要再次編譯,越復(fù)雜的sql,編譯的復(fù)雜度將越大,預(yù)編譯階段可以合并多次操作為一個(gè)操作。

預(yù)編譯語句對(duì)象可以重復(fù)利用
把一個(gè) sql 預(yù)編譯后產(chǎn)生的 PreparedStatement 對(duì)象緩存下來,下次對(duì)于同一個(gè)sql,可以直接使用這個(gè)緩存的 PreparedState 對(duì)象。

mybatis 默認(rèn)情況下,將對(duì)所有的 sql 進(jìn)行預(yù)編譯。

mysql預(yù)編譯源碼解析

mysql 的預(yù)編譯源碼在 com.mysql.jdbc.ConnectionImpl 類中,如下:

public synchronized java.sql.PreparedStatement prepareStatement(String sql,
            int resultSetType, int resultSetConcurrency) throws SQLException {
        checkClosed();

        //
        // FIXME: Create warnings if can"t create results of the given
        // type or concurrency
        //
        PreparedStatement pStmt = null;
        
        boolean canServerPrepare = true;
        
        // 不同的數(shù)據(jù)庫系統(tǒng)對(duì)sql進(jìn)行語法轉(zhuǎn)換
        String nativeSql = getProcessEscapeCodesForPrepStmts() ? nativeSQL(sql): sql;
        
        // 判斷是否可以進(jìn)行服務(wù)器端預(yù)編譯
        if (this.useServerPreparedStmts && getEmulateUnsupportedPstmts()) {
            canServerPrepare = canHandleAsServerPreparedStatement(nativeSql);
        }
        
        // 如果可以進(jìn)行服務(wù)器端預(yù)編譯
        if (this.useServerPreparedStmts && canServerPrepare) {

            // 是否緩存了PreparedStatement對(duì)象
            if (this.getCachePreparedStatements()) {
                synchronized (this.serverSideStatementCache) {
                    
                    // 從緩存中獲取緩存的PreparedStatement對(duì)象
                    pStmt = (com.mysql.jdbc.ServerPreparedStatement)this.serverSideStatementCache.remove(sql);
                    
                    if (pStmt != null) {
                        // 緩存中存在對(duì)象時(shí)對(duì)原 sqlStatement 進(jìn)行參數(shù)清空等
                        ((com.mysql.jdbc.ServerPreparedStatement)pStmt).setClosed(false);
                        pStmt.clearParameters();
                    }

                    if (pStmt == null) {
                        try {
                            // 如果緩存中不存在,則調(diào)用服務(wù)器端(數(shù)據(jù)庫)進(jìn)行預(yù)編譯
                            pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
                                    this.database, resultSetType, resultSetConcurrency);
                            if (sql.length() < getPreparedStatementCacheSqlLimit()) {
                                ((com.mysql.jdbc.ServerPreparedStatement)pStmt).isCached = true;
                            }
                            
                            // 設(shè)置返回類型以及并發(fā)類型
                            pStmt.setResultSetType(resultSetType);
                            pStmt.setResultSetConcurrency(resultSetConcurrency);
                        } catch (SQLException sqlEx) {
                            // Punt, if necessary
                            if (getEmulateUnsupportedPstmts()) {
                                pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
                                
                                if (sql.length() < getPreparedStatementCacheSqlLimit()) {
                                    this.serverSideStatementCheckCache.put(sql, Boolean.FALSE);
                                }
                            } else {
                                throw sqlEx;
                            }
                        }
                    }
                }
            } else {

                // 未啟用緩存時(shí),直接調(diào)用服務(wù)器端進(jìn)行預(yù)編譯
                try {
                    pStmt = ServerPreparedStatement.getInstance(getLoadBalanceSafeProxy(), nativeSql,
                            this.database, resultSetType, resultSetConcurrency);
                    
                    pStmt.setResultSetType(resultSetType);
                    pStmt.setResultSetConcurrency(resultSetConcurrency);
                } catch (SQLException sqlEx) {
                    // Punt, if necessary
                    if (getEmulateUnsupportedPstmts()) {
                        pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
                    } else {
                        throw sqlEx;
                    }
                }
            }
        } else {
            // 不支持服務(wù)器端預(yù)編譯時(shí)調(diào)用客戶端預(yù)編譯(不需要數(shù)據(jù)庫 connection )
            pStmt = (PreparedStatement) clientPrepareStatement(nativeSql, resultSetType, resultSetConcurrency, false);
        }
        
        return pStmt;
    }

流程圖如下所示:

mybatis之sql動(dòng)態(tài)解析以及預(yù)編譯源碼 mybatis sql 動(dòng)態(tài)解析

mybatis 在調(diào)用 connection 進(jìn)行 sql 預(yù)編譯之前,會(huì)對(duì)sql語句進(jìn)行動(dòng)態(tài)解析,動(dòng)態(tài)解析主要包含如下的功能:

占位符的處理

動(dòng)態(tài)sql的處理

參數(shù)類型校驗(yàn)

mybatis強(qiáng)大的動(dòng)態(tài)SQL功能的具體實(shí)現(xiàn)就在此。動(dòng)態(tài)解析涉及的東西太多,以后再討論。

總結(jié)

本文主要深入探究了 mybatis 對(duì) #{ } 和 ${ }的不同處理方式,并了解了 sql 預(yù)編譯。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/65626.html

相關(guān)文章

  • 通過項(xiàng)目逐步深入了解Mybatis<>

    摘要:解決方法使用數(shù)據(jù)庫連接池管理數(shù)據(jù)庫連接。向中設(shè)置參數(shù),對(duì)占位符號(hào)位置和設(shè)置參數(shù)值,硬編碼在代碼中,同樣也不利于系統(tǒng)的維護(hù)。從中遍歷結(jié)果集數(shù)據(jù)時(shí),存在硬編碼,將獲取表的字段進(jìn)行硬編碼,不利于系統(tǒng)維護(hù)。 Mybatis Mybatis 和 SpringMVC 通過訂單商品案例驅(qū)動(dòng) 官方中文地址:http://www.mybatis.org/mybati... 官方托管地址:https://...

    2bdenny 評(píng)論0 收藏0
  • 6.平凡路-單條件模糊查詢

    摘要:并且如果使用那么必須要指明值使用簡單的數(shù)據(jù)類型不好使使用接口代理模式的注解也可以 屬于MyBatis的核心之一,這里面的坑比較多,大家多多看看吧 一 模糊查詢的三種方式介紹 我會(huì)使用resultMap處理結(jié)果集數(shù)據(jù) 1.死數(shù)據(jù)的模糊查詢 映射文件 SELECT * FROM...

    tuantuan 評(píng)論0 收藏0
  • Mybatis系列】從源碼角度理解Mybatis的$和#的作用

    摘要:原因就是傳入的和原有的單引號(hào),正好組成了,而后面恒等于,所以等于對(duì)這個(gè)庫執(zhí)行了查所有的操作。類比的執(zhí)行流程和原有的我們使用的方法就是。可以理解為就是用來解析定制的符號(hào)的語句。后續(xù)的流程,就和正常的流程一致了。 前言 在JDBC中,主要使用的是兩種語句,一種是支持參數(shù)化和預(yù)編譯的PrepareStatement,能夠支持原生的Sql,也支持設(shè)置占位符的方式,參數(shù)化輸入的參數(shù),防止Sql注...

    yanwei 評(píng)論0 收藏0
  • 【JDBC系列】從源碼角度理解JDBC和Mysql預(yù)編譯特性

    摘要:我們對(duì)語句做適當(dāng)改變,就完成了注入,因?yàn)槠胀ǖ牟粫?huì)對(duì)做任何處理,該例中單引號(hào)后的生效,拉出了所有數(shù)據(jù)。查詢資料后,發(fā)現(xiàn)還要開啟一個(gè)參數(shù),讓端緩存,緩存是級(jí)別的。結(jié)論是個(gè)好東西。 背景 最近因?yàn)楣ぷ髡{(diào)整的關(guān)系,都在和數(shù)據(jù)庫打交道,增加了許多和JDBC親密接觸的機(jī)會(huì),其實(shí)我們用的是Mybatis啦。知其然,知其所以然,是我們工程師童鞋們應(yīng)該追求的事情,能夠幫助你更好的理解這個(gè)技術(shù),面對(duì)問題...

    longshengwang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<