1. Mybatis的映射配置文件Mapper.xml
1.1 輸入參數映射
1. 在Mapper映射文件中,可以通過parameterType指定SQL語句所要輸入參數的類型,類型可以是java簡單類型(String和七個基本類型以及基本類型的包裝類)、hashmap、pojo的包裝類型。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserMapper">
<select id="findUserById" parameterType="java.lang.String" resultType="entity.User">
select * from cn_user where cn_user_id=#{id}
</select>
</mapper>
2. 在查詢或其他SQL語句中,所需要的參數可能是多個,而parameterType屬性只能指定一個輸入參數的類型,也就是隻能接受一個對象,所以我們需要將這些參數封裝到一個包裝類中,這個包裝類必須符合JavaBean規範,有時候需要傳入查詢條件很複雜,可能包括用戶信息、其它信息等,而每一個數據庫表都對應這一個Java實體類,除非數據庫表的字段發生變化,一般不修改原實體類,建議使用自定義的包裝類型的pojo,在包裝類型的pojo中將複雜的查詢條件包裝進去,比如
//與數據庫User表對應的實體類,類中的每個屬性名與user表的字段一致,而且符合javabean規範
public class User implements Serializable{
private String cn_user_id;
private String cn_user_name;
private String cn_user_password;
public String getCn_user_id() {
return cn_user_id;
}
public void setCn_user_id(String cn_user_id) {
this.cn_user_id = cn_user_id;
}
public String getCn_user_name() {
return cn_user_name;
}
public void setCn_user_name(String cn_user_name) {
this.cn_user_name = cn_user_name;
}
public String getCn_user_password() {
return cn_user_password;
}
public void setCn_user_password(String cn_user_password) {
this.cn_user_password = cn_user_password;
}
@Override
public String toString() {
return "User [cn_user_id=" + cn_user_id + ", cn_user_name=" + cn_user_name + "]";
}
}
/**
* User應用擴展類,用於應變多種需求而創造的類
* 通常不在源類Uesr上直接進行修改,而是通過繼承來添加一些額外的屬性,該類也必須符合javabean規範
*
*/
public class UserCustom extends User{
//添加某些屬性
}
//通過該類所需要的幾個參數所在的擴展類的對象作爲屬性組合進來,參數輸入映射就通過該類完成
public class queryUserVo{
private UserCustom user;
//或包含其他屬性
public User getUser() {
return user;
}
public void setUser(UserCustom user) {
this.user = user;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return user.getCn_user_name();
}
}
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dao.UserDao">
<!--通過 屬性.屬性.屬性··· 的表達式從傳入的參數包裝對象中取出所需要的參數數據-->
<select id="findUserById" parameterType="queryUserVo" resultType="UserCustom">
select * from cn_user where cn_user_id=#{user.cn_user_id}
</select>
</mapper>
1.2 輸出參數映射
1. resultType
- 在Mapper映射文件中,可以通過resultType指定SQL語句查詢結果所要映射的輸出類型,類型可以是java簡單類型(String和七個基本類型以及基本類型的包裝類)、hashmap、pojo的包裝類型。使用resultType進行輸出映射,只有查詢出來的列名(可能會在SQL語句中給查詢的字段起別名)和pojo中的屬性名一致,該列纔可以映射成功。如果查詢出來的列名和pojo中的屬性名全部不一致,沒有創建pojo對象。只要查詢出來的列名和pojo中的屬性有一個一致,就會創建pojo對象,不一致的屬性的值爲null。
- 查詢出來的結果集必須只有一行且一列,可以使用簡單類型(String和7個基本類型)進行輸出映射。
- 不管是輸出的pojo單個對象還是一個列表(list中包括pojo),在mapper.xml中resultType指定的類型是一樣的。具體區別在mapper.java指定的方法返回值類型不一樣
- 輸出單個pojo對象,方法返回值是單個對象類型
- 輸出pojo對象list,方法返回值是List<Pojo>
2. resultMap
1. 在Mapper映射文件中,可以通過resultMap進行高級輸出映射。
2. 如果查詢出來的列名和pojo的屬性名不一致,通過定義一個resultMap對列名和pojo屬性名之間作一個映射關係。首先要定義resultMap,然後使用resultMap作爲statement的輸出映射類型。
<!--比如下面的SQL語句中查詢得到的字段名爲name而不是cn_user_name,與User中的屬性名無法對應,會產生問題-->
<select id="findUserById" parameterType="java.lang.String" resultType="entity.User">
select cn_user_name name from cn_user where cn_user_id=#{id}
</select>
<!--使用resultMap-->
<select id="findUserById" parameterType="java.lang.String" resultMap="username">
select cn_user_name name from cn_user where cn_user_id=#{id}
</select>
<!-- type指查詢結果集所映射的Java類型,可以是別名或全限定名
id指該resultMap的唯一標識 -->
<resultMap type="entity.User" id="username">
<!-- result標籤指定java類型中的屬性property與查詢到的字段column之間的映射關係 -->
<result column="name" property="cn_user_name"/>
</resultMap>
3. 關聯查詢,首先搞清一對一、一對多和多對多查詢,User表、NoteBook表和Note表,NoteBook表以User表的主鍵作爲外鍵進行關聯,Note表以NoteBook表的主鍵作爲外鍵進行關聯:
- 一對多:比如User用戶表與NoteBook筆記本表之間,查詢一個用戶的所有筆記本,一個用戶可以有多個筆記本,User爲查詢主表,NoteBook爲關聯表,也就是通過User表中的主鍵與NoteBook表的外鍵的關聯關係來查詢NoteBook表中的數據,所以就是一對多的關係
- 一對一:比如User用戶表與NoteBook筆記本表之間,查詢筆記本的用戶,一個筆記本只能有一個用戶,NoteBook爲查詢主表,User表爲關聯表,也就是通過NoteBook表中的外鍵與User表主鍵的關聯關係來查詢,所以就是一對一的關係; 一對一關聯查詢可以使用resultType,使用resultType實現較爲簡單,如果pojo中沒有包括查詢出來的列名,需要增加列名對應的屬性,即可完成映射,如果沒有查詢結果的特殊要求建議使用resultType。
如果使用resultMap實現一對一關聯查詢,需要單獨定義resultMap,實現有點麻煩,如果對查詢結果有特殊的要求,使用resultMap可以完成將關聯查詢映射pojo的屬性中。
resultMap可以實現延遲加載,resultType無法實現延遲加載。
- 多對多:比如User用戶表與NoteBook筆記本表與Note筆記表之間,查詢一個用戶的所有筆記本以及筆記,一個用戶會有多個筆記本,一個筆記本會包含多個筆記,User表與Note表之間無直接關聯關係,而是通過NoteBook表建立間接關聯,User爲查詢主表,NoteBook筆記本表與Note筆記表爲關聯表,通過User表中的主鍵與NoteBook表的外鍵的關聯關係來查詢用戶有多少筆記本,再通過每一個筆記本的主鍵與筆記中外鍵的關聯關係查詢到每一個筆記本的所有筆記,這就是多對多查詢
4. resultMap對於一對一查詢的使用:以User用戶表與NoteBook筆記本表爲例,NoteBook表爲查詢主表,通過NoteBook表中的User表外鍵關聯查詢User表中的數據
- 首先寫好pojo包裝映射類,因爲NoteBook表爲查詢主表,所以pojo類繼承NoteBook實體類,然後再添加所要關聯查詢的User屬性,甚至是可以將整個User類作爲pojo類的屬性,一個NoteBook對象對應一個User對象
public class NoteBook implements Serializable { private String cn_notebook_id; private String cn_user_id; private String cn_notebook_type_id; private String cn_notebook_name; private String cn_notebook_desc; private Timestamp cn_notebook_createtime; //省略get/set方法 } public class User implements Serializable{ private String cn_user_id; private String cn_user_name; private String cn_user_password; private String cn_user_token; private String cn_user_nick; //省略get/set方法 } //如果只需要查詢得到User中的部分屬性,可以直接寫屬性,而不需要將整個User對象作爲屬性 public class UserAndNoteBook1 extends NoteBook { private String cn_user_name; // private String cn_user_id; public String getCn_user_name() { return cn_user_name; } public void setCn_user_name(String cn_user_name) { this.cn_user_name = cn_user_name; } } public class UserAndNoteBook2 extends NoteBook{ private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
- 編寫Mapper映射文件中的SQL語句與映射關係:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="dao.NoteBookDao"> <!-- 通過resultType實現一對一映射 將整個查詢的結果映射到entity.UserAndNoteBook1類中 --> <select id="findNoteBookAndUser1" parameterType="String" resultType="entity.UserAndNoteBook1"> select cn_notebook.*,cn_user_name from cn_notebook,cn_user where cn_user.cn_user_id=cn_notebook.cn_user_id and cn_notebook.cn_notebook_id=#{id} </select> <!-- 通過resultMap實現一對一映射 --> <select id="findNoteBookAndUser2" parameterType="String" resultMap="UserAndNoteBook"> select cn_notebook.*,cn_user.* from cn_notebook,cn_user where cn_user.cn_user_id=cn_notebook.cn_user_id and cn_notebook.cn_notebook_id=#{id} </select> <!-- 定義映射結果集resultMap type指定將整個查詢的結果映射到entity.UserAndNoteBook2類中, id爲當前定義的resultMap在整個Mapper文件中唯一標示,通過該id屬性來引用該resultMap --> <resultMap type="entity.UserAndNoteBook2" id="UserAndNoteBook"> <!-- id:指定查詢列中的主鍵,訂單信息的中的主鍵,如果有多個主鍵,配置多個id column:數據庫表中的字段名 property:類中的屬性名 通過column和property將數據庫表中的字段映射到pojo類中指定的屬性中 --> <id column="cn_notebook_id" property="cn_notebook_id"/> <result column="cn_user_id" property="cn_user_id"/> <result column="cn_notebook_type_id" property="cn_notebook_type_id"/> <result column="cn_notebook_name" property="cn_notebook_name"/> <result column="cn_notebook_desc" property="cn_notebook_desc"/> <result column="cn_notebook_createtime" property="cn_notebook_createtime"/> <!-- association:用於映射關聯查詢單個對象的信息,一對一關聯映射查詢實現的關鍵標籤 property:要將關聯查詢的用戶信息映射到UserAndNoteBook2類中的user屬性中 javaType:指定映射的user屬性的類型 --> <association property="user" javaType="entity.User"> <id column="cn_user_id" property="cn_user_id"/> <result column="cn_user_name" property="cn_user_name"/> <result column="cn_user_password" property="cn_user_password"/> <result column="cn_user_token" property="cn_user_token"/> <result column="cn_user_nick" property="cn_user_nick"/> </association> </resultMap> </mapper>
5. resultMap對於一對多查詢的使用:以User用戶表與NoteBook筆記本表爲例,User表爲查詢主表,通過User表中的主鍵關聯查詢NoteBook表中的數據,一行User數據(或者說一個User對象)對應多行NoteBook數據(多個NoteBook對象)
- 首先寫好pojo保證類,以User表爲主表,所以應繼承User類,一個User對象對應多個NoteBook對象,所以pojo類中添加一個List<NoteBook>類型的books屬性
public class UserCustom extends User{ private List<NoteBook> books; public List<NoteBook> getBooks() { return books; } public void setBooks(List<NoteBook> books) { this.books = books; } }
- 編寫Mapper映射文件中的SQL語句與映射關係:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="dao.UserDao"> <select id="findUserWithNoteBook" parameterType="java.lang.String" resultMap="UserWithNoteBook"> SELECT cn_notebook.*, cn_user.* FROM cn_notebook, cn_user WHERE cn_user.cn_user_id = cn_notebook.cn_user_id and cn_user.cn_user_id=#{id} </select> <resultMap id="UserWithNoteBook" type="entity.UserCustom"> <id column="cn_user_id" property="cn_user_id"/> <result column="cn_user_name" property="cn_user_name"/> <result column="cn_user_password" property="cn_user_password"/> <result column="cn_user_token" property="cn_user_token"/> <!--對於一對多關係映射,應使用collection 子標籤, property指定將所有查詢到的NoteBook集合存放到books屬性對象中, ofType指定查詢到的每一條NoteBook數據都映射爲entity.NoteBook類對象 --> <collection property="books" ofType="entity.NoteBook"> <id column="cn_notebook_id" property="cn_notebook_id"/> <result column="cn_user_id" property="cn_user_id"/> <result column="cn_notebook_type_id" property="cn_notebook_type_id"/> <result column="cn_notebook_name" property="cn_notebook_name"/> <result column="cn_notebook_desc" property="cn_notebook_desc"/> <result column="cn_notebook_createtime" property="cn_notebook_createtime"/> </collection> </resultMap> </mapper>
6. resultMap對於多對多查詢的使用:以User用戶表與NoteBook筆記本表與Note筆記表爲例,User表爲查詢主表,通過User表中的主鍵關聯查詢NoteBook表中的數據,一行User數據(或者說一個User對象)對應多行NoteBook數據(多個NoteBook對象),然後再通過查詢到的每一個NoteBook中的主鍵id,關聯查詢該NoteBook有多少Note
- 首先定義pojo包裝類:User爲主表,所以pojo繼承User類;要通過NoteBook關聯查詢得到Note,所以還得對NoteBook創建一個擴展pojo類,該pojo類要添加一個List<Note>類型的notes屬性;User類的擴展pojo類就要添加一個以NoteBook的擴展pojo類作爲泛型的集合屬性books
public class Note implements Serializable{ private String cn_note_id; private String cn_notebook_id; private String cn_note_title; private String cn_note_body; //省略get/set方法 } public class NoteBookVo extends NoteBook{ private List<Note> notes; public List<Note> getNotes() { return notes; } public void setNotes(List<Note> notes) { this.notes = notes; } } public class UserVo extends User{ private List<NoteBookVo> books; public List<NoteBookVo> getBooks() { return books; } public void setBooks(List<NoteBookVo> books) { this.books = books; } }
- 編寫SQL語句以及映射關係:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="dao.UserDao"> <select id="find" parameterType="java.lang.String" resultMap="UserVo"> SELECT cn_notebook.*, cn_user.*,cn_note.* FROM cn_notebook, cn_user, cn_note WHERE cn_user.cn_user_id = cn_notebook.cn_user_id and cn_user.cn_user_id=#{id} and cn_note.cn_notebook_id=cn_notebook.cn_notebook_id </select> <resultMap id="UserVo" type="entity.UserVo"> <id column="cn_user_id" property="cn_user_id"/> <result column="cn_user_name" property="cn_user_name"/> <result column="cn_user_password" property="cn_user_password"/> <result column="cn_user_token" property="cn_user_token"/> <collection property="books" ofType="entity.NoteBookVo"> <id column="cn_notebook_id" property="cn_notebook_id"/> <result column="cn_user_id" property="cn_user_id"/> <result column="cn_notebook_type_id" property="cn_notebook_type_id"/> <result column="cn_notebook_name" property="cn_notebook_name"/> <result column="cn_notebook_desc" property="cn_notebook_desc"/> <result column="cn_notebook_createtime" property="cn_notebook_createtime"/> <collection property="notes" ofType="entity.Note"> <id column="cn_note_id" property="cn_note_id"/> <result column="cn_notebook_id" property="cn_notebook_id"/> <result column="cn_note_title" property="cn_note_title"/> <result column="cn_note_body" property="cn_note_body"/> </collection> </collection> </resultMap> </mapper>
6. 關於resultMap中的關聯查詢映射,實際上主要就是collection與association兩個子標籤的運用,這兩個子標籤之間可以互相嵌套,以應對要映射的類中包含pojo類型的屬性或者是包含集合類型的屬性,或者是屬性的屬性中包含pojo類型的屬性或包含集合類型的屬性,通過collection與association兩個子標籤的互相嵌套使用就可以解決具有複雜屬性的映射類問題。如果過屬性爲集合就使用collection,如果爲pojo類型就使用association,兩者需要之間的使用區別就在於collection標籤的屬性中指定集合中元素映射的類型使用ofType,association標籤的屬性中指定映射的類型使用javaType。
2. 動態SQL
1. 動態SQL即Mybatis對sql語句進行靈活操作,通過一些標籤進行判斷,對sql進行靈活拼接、組裝。
2. 條件判斷標籤<if test=""></if>:對輸入的參數進行判斷,如果輸入參數滿足條件才進行SQL語句拼接。如果傳入的參數是一個包裝對象,那麼就通過屬性.屬性.屬性··來對參數進行判斷
<!--無判斷sql-->
<select id="findUserById" parameterType="entity.User" resultType="entity.User">
select * from cn_user
where cn_user_id=#{id}
and cn_user_name=#{cn_user_name}
and cn_user_password=#{cn_user_password}
</select>
<!--無判斷添加if標籤判斷-->
<select id="findUserById" parameterType="entity.User" resultType="entity.User">
select * from cn_user
<!-- where標籤相當於sql中的where關鍵字,但該標籤還可以自動消除拼接的sql語句中第一個and、or關鍵字
比如,如果cn_user_name爲空那麼就會拼接and cn_user_password=#{cn_user_password},and關鍵字是多餘的
而where標籤就會消除and標籤,而且還相當於where關鍵字
-->
<where>
<!-- 判斷參數是否爲空 -->
<if test="cn_user_name!=null and cn_user_name!=''">
and cn_user_name=#{cn_user_name}
</if>
<if test="cn_user_password!=null and cn_user_password!=''">
and cn_user_password=#{cn_user_password}
</if>
</where>
</select>
3. SQL片段:將一些的頻繁出現的sql代碼塊提取出來,成爲一個SQL片段,可以提高SQL的複用性,其它的statement中就可以引用sql片段,方便程序員進行開發,通過標籤<sql id=""></sql>定義,比如將上面的判斷條件部分的提取爲一個代碼塊
<!-- 通過id定義該SQL片段在當前Mapper文件中的唯一標示,通過該id就可以被其他sql語句引用
一般來說SQL片段要基於單表,其次SQL片段中不要出現where關鍵字或者where標籤,因爲如果sql片段中
有了一個where那麼組合其他sql片段是就會出現錯誤 -->
<sql id="query_if">
<!-- 判斷參數是否爲空 -->
<if test="cn_user_name!=null and cn_user_name!=''">
cn_user_name=#{cn_user_name}
</if>
<if test="cn_user_password!=null and cn_user_password!=''">
and cn_user_password=#{cn_user_password}
</if>
</sql>
<select id="findUserById" parameterType="entity.User" resultType="entity.User">
select * from cn_user
<where>
<!-- 引用sql片段,如果引用的SQL片段在另一個mapper文件中,那麼就要在SQL片段的id
前面加上SQL片段所在的mapper文件的namespace,比如 usermapper.query_if -->
<include refid="query_if"></include>
</where>
</select>
4. 如果Mapper映射文件中sql語句的映射輸入對象是一個包裝對象,而且該包裝對象的屬性是一個List或者數組對象,可以通過foreach 標籤來對該屬性對象進行解析遍歷:比如如下兩種sql語句形式
第一種形式:select * from cn_user where cn_user_id=123 or cn_user_id=2342 or cn_user_id=34636
該形式中的where部分可變爲如下SQL片段
<where>
<!--
collection:指定輸入映射對象中的List或數組類型的屬性
separator:指定遍歷拼接的兩個片段之間進行分隔 的關鍵字,比如and、or
index:用來表示遍歷的每個元素的下標,可以通過該index屬性值來提取元素下標
item:用來表示遍歷的每個元素
open:表示遍歷拼接SQL片段之前要添加的SQL片段
close:表示遍歷拼接SQL片段之後要添加的SQL片段
-->
<foreach collection="ids" separator="or" index="index" item="id" open="(" close=")">
cn_user_id=#{id}<!--注意,這裏的#{}中必須寫與item指定的字符串相同-->
</foreach>
</where>
第二種形式:select * from cn_user where cn_user_id in (1,2,3,4)
該形式中的where部分可變爲如下SQL片段
<where>
<foreach collection="ids" separator="," index="index" item="id" open="cn_user_id in (" close=")">
#{id}<!--注意,這裏的#{}中必須寫與item指定的字符串相同-->
</foreach>
</where>
<foreach collection="" separator="" index="" item="" open="" close=""></foreach>