Mybatis學習02--XML對映器(1)
XML對映器
從上一篇的第一個程式可以看到,我們只需要在對映器Mapper.xml中新增SQL程式碼和對映定義,Mybatis就會自動將查詢對映到介面方法上。
引數注入
根據name和pwd查詢使用者,SQL程式碼如下:
select * from user where name=? and pwd=?;
如果在Java類中屬性名與資料庫列名一致,直接使用#{param name}代替?即可,Mybatis能夠匹配引數的名字,但不一致的情況下,可以用以下方法完成對映:
使用@Param註解
public User selectByNamePwd(@Param("username")String name,@Param("password")String pwd);
<select id="selectByNamePwd" resultType="com.tang.User">
select * from user
where name=#{username} and pwd=#{password}
</select>
注意引數名要與#{}中的名字一致
使用Map
public User selectByNamePwd2(Map<String,Object> map);
<select id="selectByNamePwd2" parameterType="map" resultType="com.tang.User"> select * from user where name=#{username} and pwd=#{password} </select>
Map<String,Object> map=new HashMap<String,Object>();
map.put("password","12345");
map.put("username","Su");
System.out.println(mapper.selectByNamePwd2(map));
注意在map中放入的key字串與#{}中一致
字串替換
${}和#{}的區別
'#{}中的值作為字串處理,會加引號;${}中的值不作處理,直接替換sql語句中的佔位符。$常用於資料庫物件,如表名,欄位,例如:'
'#{}在預編譯時會用?代替引數'
${}在動態解析
如下面的例子,利用${}可以使用一個方法可以實現3個方法的功能,只需要定義一個介面方法 selectByColumn 而不是3個方法
selectById(int id);
selectByName(String name);
selectByPwd(String pwd);
@Select("select * from user where ${column}=#{value}")
public User selectByColumn(@Param("column")String col,@Param("value")Object val);
System.out.println(mapper.selectByColumn("id",2));
System.out.println(mapper.selectByColumn("name","Su"));
System.out.println(mapper.selectByColumn("pwd","abcd"));
結果對映(ResultMap)
在Mapper.xml檔案中,沒有顯式指定ResultMap的情況下,Sql語句的查詢結果會簡單地對映到HashMap上,HashMap的key由資料庫列名決定,例如以下的查詢
@Select("select * from department")
public List<Department> selectDepartment();
實體類Department:
public class Department {
private String name;
private int id;
private int mid;
}
在資料庫中建表如下:
查詢結果對映到HashMap中,會存放如下的鍵值對:
<"did",1> ,<"dname","HR">, <"mid", "2"> …………
mybatis根據key查詢實體類User中的set(setDid,setDname,setMid)方法,由此帶來的問題是,如果實體類的屬性名與資料庫列名不一致,那麼set方法無法找到,最終的查詢結果返回User物件,物件中的屬性無法獲得正確的值。查詢結果如下:
depart name=null id=0 manager id=2
depart name=null id=0 manager id=1
depart name=null id=0 manager id=3
解決辦法:使用ResultMap
<resultMap id="DepartmentMap" type="Department">
<!--id為主鍵-->
<id column="did" property="id"/>
<!-- column 為資料庫中列名,property 為實體類屬性名 -->
<result column="dname" property="name"/>
<result column="mid" property="mid"/>
</resultMap>
<!-- resultMap 與前面的id對應 -->
<select id="selectDepartment2" resultMap="DepartmentMap">
select * from department
</select>
ResultMap有很多子元素
id&result
id和result都將資料庫一列對映到實體類的屬性或欄位上,id元素對應的屬性會被標記為物件識別符號,在比較物件例項中使用。
constructor
將查詢結果注入到類的構造方法中,例如
public User(Integer id, String username, int age) {
//...
}
<constructor>
<idArg column="id" javaType="int" name="id" />
<arg column="age" javaType="_int" name="age" />
<arg column="username" javaType="String" name="username" />
</constructor>
JavaType指定了引數型別,例中分別是Java.lang.Integer, int, Java.lang.String。name指定了引數名稱,在構造方法中要使用@Param註解對應,如果引數順序一致,可以省略name。
association
關聯(association)元素處理“有一個”型別的關係。mybatis有兩種方式載入關聯:
- 巢狀查詢
- 巢狀結果對映
接下來看一個例子,用上面的兩種方法實現。
資料庫建表:
根據部門id查詢部門及其主管資訊,巢狀查詢的SQL實現如下:
實體類:
@Data
public class Department {
private String name;
private int id;
private Employee manager;
private List<Employee> employees;
}
@Getter
@Setter
public class Employee {
private String name;
private int id;
private Department department;
public String toString(){
return "Employee:name="+name+" id="+id+"department"+department.getName();
}
}
在DepartmentMapper介面中新增查詢方法:
public Department selectDepartment(int did);
用mybatis實現:
<!-- 先查詢部門資訊,再根據mid查詢主管資訊 -->
<resultMap id="DepartmentManager" type="Department">
<!-- property:在實體類中屬性名 column{key=value,key=value}
key傳給sql查詢的取值,value查詢結果在資料庫中的欄位名
javaType java類,select 巢狀查詢中內層查詢的名字,與下面select id對應 -->
<id column="did" property="id"/>
<result column="dname" property="name"/>
<association property="manager" column="mid"
javaType="Employee" select="selectManager"/>
</resultMap>
<select id="selectDepartment" resultMap="DepartmentManager">
select * from department where did=#{did}
</select>
<select id="selectManager" resultMap="Employee">
select * from employee where id=#{id}
</select>
出現的問題
Error attempting to get column 'ename' from result set. Cause: java.sql.SQLException: Invalid value for getInt() - 'Su'
分析:資料庫列名'ename'與實體屬性名‘name’不匹配,而且資料庫中的順序是(id,ename,did),實體類中是(name,id,department)
用resultMap
<resultMap id="EmployeeMap" type="Employee">
<!--constructor>
<arg column="id" javaType="_int" name="id"/>
<arg column="ename" javaType="String" name="name"/>
</constructor-->
<id column="id" property="id"/>
<result column="ename" property="name"/>
</resultMap>
<select id="selectManager" resultMap="EmployeeMap">
select * from employee where id=#{id}
</select>
仍然報錯,新增無參建構函式,或者使用constructor並增加對應構造方法
<resultMap id="EmployeeMap" type="Employee">
<constructor>
<arg column="id" javaType="_int" name="id"/>
<arg column="ename" javaType="String" name="name"/>
</constructor>
<!--id column="id" property="id"/>
<result column="ename" property="name"/-->
</resultMap>
在Employee中增加構造方法和註解:
public Employee(@Param("name") String name, @Param("id") int id){
//System.out.println("constructor");
this.name=name;
this.id=id;
}
執行結果:
接下來來看另一種實現方法:結果對映
連線查詢的SQL語句實現如下:
mybatis實現
<select id="selectDepartment2" resultMap="DepartmentMap">
select d.did as did,d.dname,mid,ename from department d,employee e
where d.did=#{did} and d.mid=e.id
</select>
<resultMap id="DepartmentMap" type="Department">
<result property="id" column="did"/>
<result property="name" column="dname"/>
<!-- 關聯Department類中的成員:Employee manager -->
<association property="manager" javaType="Employee">
<result column="mid" property="id"/>
<result column="ename" property="name"/>
</association>
</resultMap>
出現的問題:
可以看到dname被當成了did進行處理,按照網上的說法,要給Java類Department新增無參建構函式和Set()方法,但我已經添加了,仍然報錯,最後發現是引數順序的問題,在Java類中是(name, id, manager),所以要改一下SQL語句,dname在前,did在後:
select d.dname,d.did as did,mid,ename from department d,employee e
where d.did=#{did} and d.mid=e.id
執行結果:
集合的對映
上面的例子是一對一關係的對映(一個部門只有一個主管),下面來看一對多關係的對映如何處理:
SQL語句查詢:查詢部門1的所有員工
mybatis實現:使用collection元素
<select id="selectDepartment3" resultMap="DepartmentMap2">
select dname,d.did,e.id as eid,ename from department d,employee e
where d.did=#{did} and d.did=e.did
</select>
<resultMap id="DepartmentMap2" type="Department">
<result property="id" column="did"/>
<result property="name" column="dname"/>
<!-- ofType:集合中元素型別 -->
<collection property="employees" ofType="Employee">
<result column="eid" property="id"/>
<result column="ename" property="name"/>
</collection>
</resultMap>
執行結果