mybatis 07: sql標籤中 “#{}” 和 “${}” 的作用和比較
“#{}”佔位符
作用
- 傳參大部分使用”#{}”,在資料庫底層使用的是:PreparedStatement預編譯處理對象
- 資料庫底層被解析為”?”,用來傳值,是安全的資料庫訪問,可以防止sql注入
- 通過在SqlMapConfig.xml添加日誌輸出配置,在後文測試輸出的結果中可以驗證
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "//mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 設置日誌輸出-->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
</configuration>
寫法
- “#{}”里參數的寫法,要參考parameterType的類型
- 如果parameterType的類型是簡單類型(8種基本類型(封裝) + String類型),則”#{}”里變數名稱任意
<!--
//根據用戶主鍵查取用戶資訊
User getById();
-->
<select id="getById" resultType="user" parameterType="int">
select
id, username, birthday, sex, address
from
users
where
id=#{asYouLike}
</select>
- 如果parameterType的類型是實體類的類型,則”#{}”里只能是類中成員變數的名稱,而且區分大小寫
//User實體類中的屬性
public class User {
private Integer id;
private String userName;
private Date birthday;
private String sex;
private String address;
}
<!--
//向用戶表中增加用戶資訊
int insert(User user);
-->
<insert id="insert" parameterType="user">
insert into
users(username, birthday, sex, address)
values(#{userName}, #{birthday}, #{sex}, #{address})
</insert>
“${}”佔位符
用於字元串的拼接和字元串的替換
字元串拼接
作用
- 一般用於模糊查詢,建議少用,因為存在sql注入風險
寫法
- “${}”中參數名稱的寫法,分兩種情況,與”#{}”的兩種情況相同,可參考之
- 注意:對於parameterType的類型是簡單類型時,”${}”里變數名稱隨便寫,但是分版本
- 如果是3.5.1及以下版本,只能寫”value”
模糊查詢示例
- 未優化前,存在sql注入風險
<!--
//根據用戶名模糊查詢用戶資訊
List<User> getByName(String name);
-->
<select id="getByName" parameterType="string" resultType="user">
select
id, username, birthday, sex, address
from
users
where username like '%${name}%'
</select>
- 優化後,使用”#{}”接受傳參,底層是preparedStatement預編譯對象,可以防止sql注入
<!--
//優化後的模糊查詢
List<User> getByNameBetter(String name);
-->
<select id="getByNameBetter" parameterType="string" resultType="user">
select
id, username, birthday, sex, address
from
users
where username like concat('%', #{name}, '%')
</select>
字元串替換(“${}”的主要作用)
需求:在users表中,根據地址或者用戶名模糊查詢用戶資訊
sql語句:
select * from users where username like ‘%模糊查詢條件%’
select * from users where address like ‘%模糊查詢條件%’
存在的問題:兩條sql語句的結構在本質上是相同的,寫兩條語句十分冗餘,可以採用替換列名的方式進行優化
UsersMapper.java介面
package com.example.mapper;
import com.example.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 數據訪問層的介面,定義對資料庫完成的CRUD的操作
*/
public interface UsersMapper {
//根據用戶名或者地址模糊查詢
List<User> getByNameOrAddress(
@Param("colName")
String colName,
@Param("userName")
String userName
);
}
- 介面分析:當介面中的方法的參數有多個時,用註解標識參數,sql標籤可通過註解中聲明的參數名獲取參數
UsersMapper.xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "//mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UsersMapper">
<!--
//根據用戶名或者地址模糊查詢
List<User> getByNameOrAddress(
@Param("colName")
String colName,
@Param("userName")
String userName);
-->
<select id="getByNameOrAddress" resultType="user">
select
id, username, birthday, sex, address
from
users
where
${colName} like concat('%', #{userName}, '%')
</select>
</mapper>
- sql標籤分析
- 當標籤對應的介面中的方法有多個參數時,標籤中的入參類型,即:parameterType,取消不寫,通過方法中註解的參數名稱獲取參數
- 用於替換時只可以使用”${}”,應該放在like前。”#{}”用來傳值,應該放在like後用來佔位傳值
測試程式碼
package com.example.mapper;
import com.example.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
public class TestUsersMapper {
//時間刷
SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd");
//SqlSession對象
SqlSession sqlSession;
//mybatis動態代理對象
UsersMapper usersMapper;
//獲取SqlSession
@Before
public void getSqlSession() throws IOException {
//讀取核心配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//創建SqlSessionFactory對象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//獲取SqlSession
sqlSession = factory.openSession();
//獲取mybatis動態代理對象
usersMapper = sqlSession.getMapper(UsersMapper.class);
}
//歸還SqlSession
@After
public void closeSession(){
sqlSession.close();
}
@Test
public void testGetByNameOrAddress(){
List<User> users = usersMapper.getByNameOrAddress("username", "小");
//List<User> users = usersMapper.getByNameOrAddress("address", "市");
users.forEach(System.out::println);
}
}
測試輸出
根據用戶名模糊查詢
Checking to see if class com.example.mapper.TestUsersMapper matches criteria [is assignable to Object]
Checking to see if class com.example.mapper.UsersMapper matches criteria [is assignable to Object]
Opening JDBC Connection
Created connection 1293462056.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28]
==> Preparing: select id, username, birthday, sex, address from users where username like concat('%', ?, '%')
==> Parameters: 小(String)
<== Columns: id, username, birthday, sex, address
<== Row: 2, 小王, 2001-07-12, 1, 蕪湖市
<== Row: 3, 小張, 1999-02-22, 1, 長沙
<== Row: 29, 小昕, 2001-03-14, 女, 忻州
<== Total: 3
Users{id=2, userName='小王', birthday=Thu Jul 12 00:00:00 CST 2001, sex='1', address='蕪湖市'}
Users{id=3, userName='小張', birthday=Mon Feb 22 00:00:00 CST 1999, sex='1', address='長沙'}
Users{id=29, userName='小昕', birthday=Wed Mar 14 00:00:00 CST 2001, sex='女', address='忻州'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28]
Returned connection 1293462056 to pool.
Process finished with exit code 0
根據地址模糊查詢
Checking to see if class com.example.mapper.TestUsersMapper matches criteria [is assignable to Object]
Checking to see if class com.example.mapper.UsersMapper matches criteria [is assignable to Object]
Opening JDBC Connection
Created connection 1293462056.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28]
==> Preparing: select id, username, birthday, sex, address from users where address like concat('%', ?, '%')
==> Parameters: 市(String)
<== Columns: id, username, birthday, sex, address
<== Row: 2, 小王, 2001-07-12, 1, 蕪湖市
<== Row: 7, 學委, 2001-05-13, 2, 平頂山市
<== Total: 2
Users{id=2, userName='小王', birthday=Thu Jul 12 00:00:00 CST 2001, sex='1', address='蕪湖市'}
Users{id=7, userName='學委', birthday=Sun May 13 00:00:00 CST 2001, sex='2', address='平頂山市'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@4d18aa28]
Returned connection 1293462056 to pool.
Process finished with exit code 0
測試結果分析
- sql標籤在底層分別被解析為
==> Preparing: select id, username, birthday, sex, address from users where username like concat('%', ?, '%')
==> Parameters: 小(String)
==> Preparing: select id, username, birthday, sex, address from users where address like concat('%', ?, '%')
==> Parameters: 市(String)
- 由上可知
- 標籤中的${colName}分別被替換成了傳入的”username”和”address”,起到了替換作用
- “#{}”被解析成”?”,#{userName}分別拿到值:小(String)和市(String),起到佔位傳值作用