天天看點

MyBatis_MyBatis之參數傳遞

1.引入

   我們通過上一節課實作了對一個實體對象的基本的增删查改操作,其中,我們在映射檔案和測試的代碼中添加了一下參數。那麼接下來我們就一起來看看MyBatis中參數是如何傳遞的。

2.參數的分類以及參數是傳遞

(1).單個參數:可以接受基本類型,對象類型,集合類型的值。這種情況MyBatis可直接使用這個參數,不需要經過任何處理。

          規則:#{參數名/任意名}:取出參數值。

          示例:如:

//方法:
public Employee getEmpById(Integer id);


//調用時候參數的傳遞:
EmployeeMapperAnnotation mapper = openSession.getMapper(EmployeeMapperAnnotation.class);
Employee empById = mapper.getEmpById(1);


//參數傳遞給SQL
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where id = #{id}
</select>

//這裡直接使用:#{參數名/任意名}:取出參數值。
           

(2).多個參數:

         按照我們上面所寫的單個參數,我們照着單個參數的形式嘗試着寫了一下多個參數的形式,發現結果如下:

//方法
public Employee getEmpByIdAndLastName(Integer id,String lastName);

//調用測試:
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndLastName(1, "tom");
System.out.println(employee);


//傳遞給的SQL
<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where id = #{id} and last_name=#{lastName}
 </select>


//結果報錯:org.apache.ibatis.binding.BindingException: Parameter 'id' not found. 
	      Available parameters are [1, 0, param1, param2]
           

通過上面的操作,也就是說MyBatis對于多個參數的傳遞是做了比較特殊的操作,那麼是什麼樣子的特殊操作呢?

       任意多個參數,都會被MyBatis重新包裝成一個Map傳入。Map的key是param1,param2,0,1…,值就是參數的值。也就是說:多個參數會被封裝成 一個map,其中

         key:param1...paramN,或者參數的索引也可以,即0,1

         value:傳入的參數值

         #{}就是從map中擷取指定的key的值;但是實際上我們應該擷取的是key所對應的值。

我們進行一下修改:

//方法
public Employee getEmpByIdAndLastName(Integer id,String lastName);

//調用測試:
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndLastName(1, "tom");
System.out.println(employee);


//傳遞給的SQL
<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where id = #{param1} and last_name=#{param2}
 </select>


           

但是,通過這樣的修改以後參數是可以取出來了。但是我們發現當參數比較多的時候,每一個都寫param或者是1,2,3這樣沒有見名知意的效果。MyBatis提供了一個叫做命名參數的來解決這一個問題。(推薦使用)

【命名參數】:明确指定封裝參數時map的key;@Param("id")

                         多個參數會被封裝成 一個map,

                         key:使用@Param注解指定的值

                        value:參數值

                       #{指定的key}取出對應的參數值

如下:

//方法
public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);

//調用測試:
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndLastName(1, "tom");
System.out.println(employee);


//傳遞給的SQL
<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where id = #{id} and last_name=#{lastName}
 </select>


           

(3).傳遞實體對象以及Map傳遞參數

         如果多個參數正好是我們業務邏輯的資料模型,我們就可以直接傳入pojo;

          規則:#{屬性名}:取出傳入的pojo的屬性值    

        Map:

        如果多個參數不是業務模型中的資料,沒有對應的pojo,不經常使用,為了友善,我們也可以傳入map。

        規則:  #{key}:取出map中對應的值。

       TO:

       如果多個參數不是業務模型中的資料,但是經常要使用,推薦來編寫一個TO(Transfer Object)資料傳輸對象

       Page{

       int index;

       int size;

       }

       如:使用一個map作為多參數傳遞使用

//方法
public Employee getEmpByMap(Map<String, Object> map);

//方法測試
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);			
Map<String, Object> map = new HashMap<>();
map.put("id", 2);
map.put("lastName", "Tom");
map.put("tableName", "tbl_employee");
Employee employee = mapper.getEmpByMap(map);


//SQL
<select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from ${tableName} where id=${id} and last_name=#{lastName}
</select>
 
           

3.如何選擇使用什麼作為參數傳遞:

針對不同的使用場景,應該如何選擇使用的參數?

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(collection),如果是List還可以使用這個key(list)
				 數組(array)

public Employee getEmpById(List<Integer> ids);
	取值:取出第一個id的值:   #{list[0]}
	
           

4.MyBatis是如何處理參數的

MyBatis是如何處理參數的?

總結:參數多時會封裝map,為了不混亂,我們可以使用@Param來指定封裝時使用的key;
#{key}就可以取出map中的值;

(@Param("id")Integer id,@Param("lastName")String lastName);
ParamNameResolver解析參數封裝map的;
//1、names:{0=id, 1=lastName};構造器的時候就确定好了

	确定流程:
	1.擷取每個标了param注解的參數的@Param的值:id,lastName;  指派給name;
	2.每次解析一個參數給map中儲存資訊:(key:參數索引,value:name的值)
		name的值:
			标注了param注解:注解的值
			沒有标注:
				1.全局配置:useActualParamName(jdk1.8):name=參數名
				2.name=map.size();相當于目前元素的索引
	{0=id, 1=lastName,2=2}
				

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;
    }
  }
}
           

5.參數擷取中#和$的差別

#{}:可以擷取map中的值或者pojo對象屬性的值;

${}:可以擷取map中的值或者pojo對象屬性的值;

select * from tbl_employee where id=${id} and last_name=#{lastName}

Preparing:

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=OTHER};

        2、jdbcTypeForNull=NULL

            <setting name="jdbcTypeForNull" value="NULL"/>

繼續閱讀