單個參數:mybatis不會做特殊處理
#{參數名/任意名}:取出參數值。
多個參數:mybatis會做特殊處理。
多個參數會被封裝成 一個map, 1. key:param1...paramN,或者參數的索引也可以。 2. value:傳入的參數值 #{}就是從map中獲取指定的key的值;
若多個參數直接使用bean屬性名
方法:public Employee getEmpByIdAndLastName(Integer id,String lastName); 取值:#{id},#{lastName}
則拋出異常:
org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [1, 0, param1, param2]
多個參數取值的方法
命名參數 :
在接口定義的方法中明確指定封裝參數時map的key 如:getEmployee(@Param("id")Integer id, @Param("lastName")String lastName) 多個參數會被封裝成 一個map, key:使用@Param註解指定的值 value:參數值 #{指定的key}取出對應的參數值
POJO:
如果多個參數正好是我們業務邏輯的數據模型,我們就可以直接傳入pojo (將零散屬性直接封裝bean,這樣相當於傳一個參數); #{屬性名}:取出傳入的pojo的屬性值
Map:
如果多個參數不是業務模型中的數據,沒有對應的pojo, 不經常使用,爲了方便,我們也可以傳入map 如:map.put("id", employee.getId()); map.put("lastName", employee.getLastName()); #{id}, #{lastName}:取出map中對應的值
To:
如果多個參數不是業務模型中的數據,但是經常要使用,
推薦來編寫一個TO(Transfer Object)數據傳輸對象Page{ int index; int size; }
思考
public Employee getEmp(@Param("id")Integer id,String lastName);
取值:id==>#{id/param1} lastName==>#{param2}
public Employee getEmp(Integer id,@Param("e")Employee emp);
取值:id==>#{param1} lastName===>#{param2.lastName/e.lastName}
特別注意:
如果是Collection(List、Set)也會特殊處理。
也是把傳入的list或者數組封裝在map中。
key:(collection),
如果是List還可以使用這個key(list)
數組(array)
public Employee getEmpById(List<Integer> ids);
取值:取出第一個id的值: #{list[0]}
結合源碼,mybatis如何處理參數
確定流程:
1.獲取每個標了param註解的參數的@Param的值:id,lastName; 賦值給name;
2.每次解析一個參數給map中保存信息:(key:參數索引,value:name的值)
name用來保存參數的名字,分爲使用註解和不使用兩種情況
name的值:
標註了param註解:註解的值
沒有標註:
1.全局配置:useActualParamName(jdk1.8):name=參數名
2.name=map.size();相當於當前元素的索引
{0=id, 1=lastName,2=2}
Mybatis獲取參數的方法如下
/*getEmployee(@Param("id")Integer id, @Param("lastName")String lastName, String email)
args【1,"Tom",'hello'】 */
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
//1、參數爲null直接返回
if (args == null || paramCount == 0) {
return null;
//2、如果只有一個元素,並且沒有Param註解;args[0]:單個參數直接返回
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
//3、多個元素或者有Param標註
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
//4、遍歷names集合;{0=id, 1=lastName,2=2}
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//names集合的value作爲key; names集合的key又作爲取值的參考args[0]:args【1,"Tom"】:
//eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)param
//額外的將每一個參數也保存到map中,使用新的key:param1...paramN
//效果:有Param註解可以#{指定的key},或者#{param1}
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
}
參數值的獲取
#{}:可以獲取map中的值或者pojo對象屬性的值;
${}:也可以獲取map中的值或者pojo對象屬性的值;
select * from tbl_employee where id=${id} and last_name=#{lastName}
控制檯: select * from tbl_employee where id=2 and last_name=?
由此可見區別如下
#{}:是以預編譯的形式,將參數設置到sql語句中;
PreparedStatement;防止sql注入
${}:取出的值直接拼裝在sql語句中;會有安全問題;
大多情況下,我們去參數的值都應該去使用#{};
什麼情況下用到${}?
原生jdbc不支持佔位符的地方我們就可以使用${}進行取值
比如分表、排序。。。;按照年份分表拆分
select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}
#{}更豐富的用法
規定參數的一些規則:
javaType、 jdbcType、 mode(存儲過程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未來準備支持的功能);
jdbcType通常需要在某種特定的條件下被設置:
在我們數據爲null的時候,有些數據庫可能不能識別mybatis對null的默認處理。
比如Oracle(報錯);
JdbcType OTHER:無效的類型;
因爲mybatis對所有的null都映射的是原生Jdbc的OTHER類型,
oracle不能正確處理;
由於全局配置中:jdbcTypeForNull=OTHER;oracle不支持;兩種辦法
1、#{email,jdbcType=NULL};
2、jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>