在 Joomla 4 中, Joomla 數(shù)據(jù)庫查詢轉(zhuǎn)變?yōu)槭褂妙A(yù)編譯語句,本文旨在闡明我們這樣做的原因和方式。
簡(jiǎn)單的來說,預(yù)編譯語句相比之前的字符串查詢語句將會(huì)有兩大優(yōu)勢(shì):
通常我們的一條sql在db接收到最終執(zhí)行完畢返回可以分為下面三個(gè)過程:
我們把這種普通語句稱作Immediate Statements。
但是很多情況,我們的一條sql語句可能會(huì)反復(fù)執(zhí)行,或者每次執(zhí)行的時(shí)候只有個(gè)別的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。如果每次都需要經(jīng)過上面的詞法語義解析、語句優(yōu)化、制定執(zhí)行計(jì)劃等,則效率就明顯不行了。
所謂預(yù)編譯語句就是將這類語句中的值用占位符替代,可以視為將sql語句模板化或者說參數(shù)化,一般稱這類語句叫Prepared Statements或者Parameterized Statements 。
預(yù)編譯語句的優(yōu)勢(shì)歸納為:一次編譯、多次運(yùn)行,省去了解析優(yōu)化等過程;此外預(yù)編譯語句能防止sql注入。
joomla在底層的JDatabaseDriver類中已經(jīng)幫我們實(shí)現(xiàn)好了預(yù)編譯語句這項(xiàng)功能,開發(fā)者只需要在寫查詢語句的時(shí)候調(diào)用即可。以下面的查詢語句為例:
正常的SQL語句寫法:
$query =$this->db->getQuery(true)
->select($this->db->quoteName(array('id','password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') .'=' .$this->db->quote($credentials['username']));
轉(zhuǎn)換為預(yù)編譯語句的SQL語句寫法:
$query =$this->db->getQuery(true)
->select($this->db->quoteName(array('id','password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') .' = :username')
->bind(':username',$credentials['username']);
通過在where中使用占位符,在bind中進(jìn)行參數(shù)綁定就實(shí)現(xiàn)了預(yù)編譯的功能。另外,請(qǐng)注意最后的bind方法,在這里我們不再使用$db的quote(加引號(hào))的方法,因?yàn)閿?shù)據(jù)庫驅(qū)動(dòng)將自動(dòng)的幫我們加上。
JDatabaseDriver 會(huì)對(duì)一些函數(shù)自動(dòng)使用預(yù)編譯語句,如 whereIn()和whereNotIn()方法將自動(dòng)的使用。
典型的代碼如下:
$query =$this->db->getQuery(true)
->select($this->db->quoteName(array('id, password')))
->from($this->db->quoteName('#__users'))
->whereIn($this->db->quoteName('id'), [ 1, 2, 3 ]);
這個(gè)查詢將會(huì)轉(zhuǎn)換為預(yù)編譯語句的SQL如下:
SELECT
`id`, `password`
FROM
`#__users`
WHERE
`id`IN (
:preparedArray1,
:preparedArray2,
:preparedArray3
);
其中的占位符 :preparedArray1-3 將在執(zhí)行時(shí)填充 1,2,3。
通過在where中使用占位符,在bind中進(jìn)行參數(shù)綁定就實(shí)現(xiàn)了預(yù)編譯的功能。另外,請(qǐng)注意最后的bind方法,在這里我們不再使用$db的quote(加引號(hào))的方法,因?yàn)閿?shù)據(jù)庫驅(qū)動(dòng)將自動(dòng)的幫我們加上。
另外,以下函數(shù)接受數(shù)組以減少函數(shù)調(diào)用開銷
另外在編程中使用預(yù)編譯的一個(gè)好處是可以方便的使用循環(huán),下面是一段實(shí)例代碼:
$listOfUsernames = ['admin','user1' ];
$query =$this->db->getQuery(true)
->select($this->db->quoteName(array('id','password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') .' = :username')
->bind(':username',$username);
foreach($listOfUsernames as $name)
{
$username =$name;
$this->db->setQuery($query);
$user =$this->db->loadObject();
print_r($user);
}
在上面的循環(huán)中,給$username賦值,然后重新調(diào)用setQuery方法來獲得數(shù)據(jù),非常的方便。
同樣,也可以使用數(shù)據(jù)組來完成參數(shù)的綁定,代碼如下:
$query =$this->db->getQuery(true)
->select($this->db->quoteName(array('id','password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') .' = :username')
->where($this->db->quoteName('id') .' = :id')
->bind([':username',':id'], [$credentials['username'], 42], [Joomla\Database\ParameterType::STRING, Joomla\Database\ParameterType::INTEGER]);
在上面的代碼中添加 username 和 id 作為綁定參數(shù),并為每個(gè)變量設(shè)置正確的 ParameterType。也可以對(duì)所有綁定值和 ParameterType 使用一個(gè)變量。 代碼如下:
$query =$this->db->getQuery(true)
->select($this->db->quoteName(array('id','password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') .' = :username')
->where($this->db->quoteName('password') .' = :password')
->bind([':username',':password], $credentials['username']);
在上面參數(shù) :username 和 :password 設(shè)置為相同的值和默認(rèn)的參數(shù)類型。
更多建議: