Mybatis入門(三)
動態sql
MyBatis常用OGNL表示式
-
e1 or e2
-
e1 and e2
-
e1 == e2,e1 eq e2
-
e1 != e2,e1 neq e2
-
e1 lt e2:小於
-
e1 lte e2:小於等於,其他gt(大於),gte(大於等於)
-
e1 in e2
-
e1 not in e2
-
e1 + e2,e1 * e2,e1/e2,e1 - e2,e1%e2
-
!e,not e:非,求反
-
e.method(args)呼叫物件方法
-
e.property物件屬性值
-
e1[ e2 ]
-
@[email protected](args)呼叫類的靜態方法
-
@[email protected]呼叫類的靜態欄位值
1、if
-
if標籤:判斷語句,用於進行邏輯判斷的。
test屬性:用來編寫表示式,支援ognl
案例:查詢男性使用者,如果輸入了使用者名稱,按使用者名稱模糊查詢,如果沒有輸入使用者名稱,就查詢所有男性使用者
定義介面
在UserMapper介面中定義方法:
/** * 通過使用者名稱進行模糊查詢,如果沒有使用者名稱,查詢所有男性使用者 * @param userName * @return */ List<User> queryUsersLikeUserName(@Param("userName")String userName);
在UserMapper對映檔案中,定義介面方法對應的Statement
<select id="queryUsersLikeUserName" resultType="User"> select * from tb_user where sex = 1 <!-- if標籤:用來進行判斷 test屬性:編寫ognl表示式 --> <if test="userName!=null and userName.trim()!=''"> and user_name like '%' #{userName} '%' </if> </select>
測試
@Test
public void testQueryUsersLikeUserName(){
List<User> list = userMapper.queryUsersLikeUserName("zhang");
for (User user : list) {
System.out.println(user);
}
}
2、choose when otherwise
-
choose標籤:條件選擇
-
when子標籤:編寫條件,不管有多少個when條件,一旦其中一個條件成立,後面的when條件都不執行。
test屬性:編寫ognl表示式
otherwise子標籤:當所有條件都不滿足時,才會執行該條件
需求:編寫一個查詢方法,設定兩個引數,一個是使用者名稱,一個是年齡。
根據使用者名稱或者年齡查詢所有男性使用者,如果輸入了使用者名稱則按照使用者名稱模糊查詢,否則就按照年齡查詢,兩個條件只能成立一個,如果都不輸入就查詢使用者名稱為“zhangsan”的使用者
定義介面
在UserMapper介面中,定義介面方法:
/**
* 根據使用者名稱或者年齡查詢男性使用者,如果有使用者名稱就按使用者名稱查詢,否則按年齡查詢,如果都沒有,就查詢名字是zhangsan的男性使用者
* @param userName
* @param age
* @return
*/
List<User> queryUserListByUserNameOrAge(@Param("userName")String userName,@Param("age")Integer age);
在UserMapper.xml中,定義介面方法對應的Statement
<select id="queryUserListByUserNameOrAge" resultType="User">
select * from tb_user where sex = 1
<choose>
<when test="userName!=null and userName.trim()!=''">
and user_name like '%' #{userName} '%'
</when>
<when test="age!=null">
and age = #{age}
</when>
<otherwise>
and user_name = "zhangsan"
</otherwise>
</choose>
</select>
測試
@Test
public void testQueryUserListByUserNameOrAge(){
List<User> list = userMapper.queryUserListByUserNameOrAge("zhang",20);
for (User user : list) {
System.out.println(user);
}
}
3、 Where
where標籤:可以自動將動態sql中多出來的一個and或者or去除
案例:查詢所有使用者,如果輸入了使用者名稱按照使用者名稱進行模糊查詢,如果輸入年齡,按年齡進行查詢,如果兩者都輸入,兩個條件都要成立
定義介面
在UserMapper介面中,定義介面方法:
/**
* 根據使用者名稱和年齡查詢使用者。
* @param userName
* @param age
* @return
*/
List<User> queryUserListByUserNameAndAge(@Param("userName")String userName,@Param("age")Integer age);
在UserMapper.xml中,定義介面方法對應的Statement
<select id="queryUserListByUserNameAndAge" resultType="User">
select * from tb_user
<where>
<if test="userName!=null and userName.trim()!=''">
user_name like '%' #{userName} '%'
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>
測試
@Test
public void testQueryUserListByUserNameAndAge(){
List<User> list = userMapper.queryUserListByUserNameAndAge("", 30);
for (User user : list) {
System.out.println(user);
}
}
4、Set
set標籤:
可以自動新增一個set關鍵字,並且會將動態sql最後多餘的逗號去除
案例:修改使用者資訊,如果引數user中的某個屬性為null,則不修改
定義介面:
在UserMapper介面中,定義介面方法
/**
* 修改使用者
* @param user
*/
void updateUserSelective(User user);
在UserMapper.xml中,定義介面方法對應的Statement
<update id="updateUserSelective">
update tb_user
<set>
<if test="userName!=null and userName.trim()!=''">
user_name = #{userName},
</if>
<if test="password!=null and password.trim()!=''">
password = #{password},
</if>
<if test="name!=null and name.trim()!=''">
name = #{name},
</if>
<if test="age!=null">
age = age
</if>
</set>
where id = #{id}
</update>
測試
@Test
public void testUpdateUserSelective(){
User user = new User();
user.setUserName("admin11");
user.setPassword("654321");
user.setName("使用者11");
//user.setAge(20);
user.setId(9l);
userMapper.updateUserSelective(user);
}
5、foreach
foreach標籤:遍歷集合或者陣列
collection屬性:接收的集合或者陣列引數
item屬性:集合或者陣列引數中的每一個元素
separator屬性:標籤分隔符
open屬性:以什麼開始
close屬性:以什麼結束
案例:按照多個id查詢使用者資訊
介面
在UserMapper介面中,定義介面方法:
/**
* 通過多個使用者id查詢多個使用者
* @param ids
* @return
*/
List<User> queryUserListByIds(@Param("ids")Long[] ids);
在UserMapper.xml中,定義介面方法對應的Statement
<select id="queryUserListByIds" resultType="User">
select * from tb_user where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</select>
測試
@Test
public void testQueryUserListByIds(){
Long [] ids = new Long[] {1l,2l,3l};
List<User> users = userMapper.queryUserListByIds(ids);
for (User user : users) {
System.out.println(user);
}
}
總結
1、If標籤:條件判斷
test屬性:編寫ognl表示式
2、choose標籤:條件選擇
when子標籤:用於條件選擇,一旦有一個when成立,後續不再執行。
otherwise子標籤:所有條件不成立,執行該條件。
3、where標籤:用於sql動態條件拼接,新增where關鍵字,可以將動態sql多餘的第一個and或者or去除。
4、set標籤: 用於更新語句的拼接,新增set關鍵字,並可以將動態sql中多餘的逗號去除
5、foreach標籤:用於遍歷引數中的集合
collection屬性:引數中的集合
item屬性:表示集合中的某個元素
separator屬性:分隔符
open:以什麼開始
close:以什麼結束
高階查詢(OrderMapper.xml)
1、一對一查詢
需求:通過訂單編號20140921003查詢出訂單資訊,並查詢出下單人資訊
編寫介面
public interface OrderMapper {
/**
* 通過訂單編號查詢訂單和使用者
* @param orderNumber:訂單編號
* @return
*/
Order queryOrderAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
}
建立Order
/**
* 訂單表
*
*/
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
private User user;
@Override
public String toString() {
return "Order{" +
"id=" + id +
", userId=" + userId +
", orderNumber='" + orderNumber + '\'' +
", user=" + user +
'}';
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getOrderNumber() {
return orderNumber;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
}
編寫OrderMapper.xml
注意:一旦涉及到巢狀對映,一定要設定手動設定為自動對映。不管是Order的自動對映還是User的自動對映都需要手動設定為true
<mapper namespace="cn.itcast.mapper.OrderMapper">
<!--
配置自定義結果集
id屬性:自定義結果集的唯一標識
type屬性:結果集型別
autoMapping屬性:多表查詢時,必須設定為true,Order物件和tb_order表的屬性和欄位才會進行自動對映
-->
<resultMap id="orderAndUserResultMap" type="Order" autoMapping="true">
<!--配置Order的主鍵對映-->
<id column="id" property="id"></id>
<!--
association標籤:用於對一的對映
property屬性:類中的關聯屬性的名稱
javaType屬性:屬性對應的型別
autoMapping屬性:autoMapping屬性:多表查詢時,必須設定為true,User物件和tb_user表的屬性和欄位才會進行自動對映
-->
<association property="user" javaType="User" autoMapping="true">
<!--配置User的主鍵對映-->
<id column="uid" property="id"></id>
</association>
</resultMap>
<!--一對一查詢-->
<select id="queryOrderAndUserByOrderNumber" resultMap="orderAndUserResultMap">
SELECT
*,u.id as uid
FROM
tb_order o
INNER JOIN tb_user u ON o.user_id = u.id
WHERE
o.order_number = #{orderNumber}
</select>
</mapper>
全域性配置檔案管理對映檔案
在mybatis-config.xml中新增程式碼如下:
<mapper resource="OrderMapper.xml"/>
編寫測試類
@Test
public void queryOrderAndUserByOrderNumber() {
Order order = orderMapper.queryOrderAndUserByOrderNumber("20140921003");
System.out.println(order);
}
2、 一對多查詢
一對多查詢:通過訂單編號20140921001查詢訂單,並查詢出下單人資訊以及查詢出訂單詳情
思路: 訂單 : 訂單詳情 = 1 : n(體現在pojo物件中,就是在Order物件中新增OrderDetail物件的集合)
SQL:查詢出兩條記錄,除了item_id和total_price不同外,其他都一樣
修改Order
在Order類新增List<OrderDetial>屬性,並新增get、set方法和toString方法:
/**
* 訂單表
*
*/
public class Order {
private Integer id;
private Long userId;
private String orderNumber;
//配置Order和User一對一的關係
private User user;
//配置Order和Orderdetail一對多的關係
private List<Orderdetail> orderdetails;
@Override
public String toString() {
return "Order{" +
"id=" + id +
", userId=" + userId +
", orderNumber='" + orderNumber + '\'' +
", user=" + user +
", orderdetails=" + orderdetails +
'}';
}
public List<Orderdetail> getOrderdetails() {
return orderdetails;
}
public void setOrderdetails(List<Orderdetail> orderdetails) {
this.orderdetails = orderdetails;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
編寫介面方法
/**
* 通過訂單編號查詢訂單、使用者和訂單詳情
* @param orderNumber:訂單編號
* @return
*/
Order queryOrderAndUserAndOrderdetailByOrderNumber(@Param("orderNumber") String orderNumber);
編寫statement
<!--一對多自定義結果集-->
<resultMap id="orderAndUserAndOrderdetailsResultMap" type="Order" autoMapping="true">
<id column="id" property="id"></id>
<!--一對一-->
<association property="user" javaType="User" autoMapping="true">
<id column="uid" property="id"></id>
</association>
<!--一對多-->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
<id column="detail_id" property="id"></id>
</collection>
</resultMap>
<!--一對多查詢-->
<select id="queryOrderAndUserAndOrderdetailByOrderNumber" resultMap="orderAndUserAndOrderdetailsResultMap">
SELECT
*,u.id as uid,od.id as detail_id
FROM
tb_order o
INNER JOIN tb_user u ON o.user_id = u.id
INNER JOIN tb_orderdetail od on od.order_id = o.id
WHERE
o.order_number = #{orderNumber}
</select>
測試
@Test
public void queryOrderAndUserAndOrderdetailByOrderNumber(){
Order order = orderMapper.queryOrderAndUserAndOrderdetailByOrderNumber("20140921001");
System.out.println(order);
}
注意:在使用別名查詢來對映主鍵的時候,一定要手動在sql語句中自行定義別名
3、多對多查詢
多對多查詢:通過訂單號20140921001查詢訂單,查詢出下單人資訊並且查詢出訂單詳情以及商品資料
思路:訂單:訂單詳情 = 1 : n(體現在pojo物件中就是在Order物件中新增OrderDetail物件的集合)
訂單詳情:商品 = 1 : 1(體現在pojo物件中就是在OrderDetail物件中新增Item物件)
Sql:#通過訂單號20140921001查詢訂單,查詢出下單人資訊並且查詢出訂單詳情中的商品資料
通過訂單號20140921001查詢訂單,查詢出下單人資訊並且查詢出訂單詳情中的商品資料。
1、由於是通過Order對一查詢User,因此要在Order的實體中設定對一User的屬性配置,然後還要對多去查詢Orderdetail,因此要在Order實體類中設定對多detailList的屬性來進行查詢,並新增get、set方法:之前已設定過
2、還要通過Orderdetail去對一查詢Item,因此要在Orderdetail中去設定對一Item的屬性,並新增get、set方法:
修改Orderdetail
在Orderdetail中新增item屬性:
public class Orderdetail {
private Integer id;
private Double totalPrice;
private Integer status;
//配置Orderdetail和Item一對一的關係
private Item item;
@Override
public String toString() {
return "Orderdetail{" +
"id=" + id +
", totalPrice=" + totalPrice +
", status=" + status +
", item=" + item +
'}';
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
編寫介面方法
/**
* 通過訂單編號查詢訂單、使用者和訂單詳情以及商品
* @param orderNumber
* @return
*/
Order queryOrderAndUserAndOrderdetailAndItemByOrderNumber(@Param("orderNumber") String orderNumber);
編寫statement
OrderMapper配置(通過在collection標籤中巢狀使用association標籤):
<resultMap id="orderAndUserAndOrderdetailsAndItemResultMap" type="Order" autoMapping="true">
<id column="id" property="id"></id>
<!--一對一-->
<association property="user" javaType="User" autoMapping="true">
<id column="uid" property="id"></id>
</association>
<!--一對多-->
<collection property="orderdetails" javaType="List" ofType="Orderdetail" autoMapping="true">
<id column="detail_id" property="id"></id>
<!--訂單詳情和商品的一對一的關係-->
<association property="item" javaType="Item" autoMapping="true">
<id column="iid" property="id"></id>
</association>
</collection>
</resultMap>
<!--多對多查詢-->
<select id="queryOrderAndUserAndOrderdetailAndItemByOrderNumber" resultMap="orderAndUserAndOrderdetailsAndItemResultMap">
SELECT
*,u.id as uid,od.id as detail_id,i.id as iid
FROM
tb_order o
INNER JOIN tb_user u ON o.user_id = u.id
INNER JOIN tb_orderdetail od on od.order_id = o.id
INNER JOIN tb_item i on i.id = od.item_id
WHERE
o.order_number = #{orderNumber}
</select>
測試
@Test
public void queryOrderAndUserAndOrderdetailAndUserByOrderNumber(){
Order order = orderMapper.queryOrderAndUserAndOrderdetailAndItemByOrderNumber("20140921001");
System.out.println(order);
}
4、 resultMap的繼承
如果說有的resultMap中的資料其他地方也要用到,那麼可以使用繼承的方法來複用即可:
5、 高階查詢的整理
resutlType無法幫助我們自動的去完成對映,所以只有使用resultMap手動的進行對映
resultMap:
type 結果集對應的資料型別
id 唯一標識,被引用的時候,進行指定
autoMapping 開啟自動對映
extends 繼承
子標籤:
association:配置對一的對映
property 定義物件的屬性名
javaType 屬性的型別
autoMapping 開啟自動對映
collection:配置對多的對映
property 定義物件的屬性名
javaType 集合的型別
ofType 集合中的元素型別
autoMapping 開啟自動對映
延遲載入
何為延遲載入?
延遲載入:就是在需要用到資料時才進行載入,不需要用到資料時就不載入資料。延遲載入也稱懶載入.
好處:先從單表查詢,需要時再從關聯表去關聯查詢,大大提高資料庫效能,因為查詢單表要比關聯查詢多張錶速度要快。。
壞處:因為只有當需要用到資料時,才會進行資料庫查詢,這樣在大批量資料查詢時,因為查詢工作也要消耗時間,所以可能造成使用者等待時間變長,造成使用者體驗下降。
需求:
延遲載入需求:通過訂單編號20140921001查詢order並延遲載入user
如果通過訂單編號查詢order並且查詢user資訊,在正常情況下的查詢語句應該是:
如果改成延遲載入,也就意味著,先查詢order,等需要的時候再去查詢user,那就相當於將上面的一條語句變成了兩條語句:
1、通過訂單編號查詢order
2、通過查詢出來的order中的user_id查詢user
延遲載入案例
編寫介面方法
/**
* 延遲載入
* @param orderNumber
* @return
*/
Order queryOrderUserLazy(@Param("orderNumber")String orderNumber);
編寫statement
兩個statement就分別相當於兩條查詢order和查詢user的sql語句,然後通過resultMap將延遲載入的查詢user的statement關聯到查詢查詢order的statement上。使得在查詢order之後,可以延遲載入user.
<!--通過Order延遲載入User-->
<resultMap id="orderUserLazyResultMap" type="Order">
<!--
select屬性:呼叫指定sql語句來執行延遲載入
column屬性:延遲載入的sql語句中所需的引數
-->
<association property="user" javaType="User" select="queryUserByIdOfOrder" column="{id=user_id}"></association>
</resultMap>
<!--通過訂單編號查詢訂單-->
<select id="queryOrderUserLazy" resultMap="orderUserLazyResultMap">
select * from tb_order where order_number = #{orderNumber}
</select>
<select id="queryUserByIdOfOrder" resultType="User">
select * from tb_user where id = #{id}
</select>
測試
@Test
public void queryOrderUserLazy(){
Order order = orderMapper.queryOrderUserLazy("20140921001");
System.out.println(order.getId()+"..."+order.getOrderNumber()+"..."+order.getUserId());
System.out.println(order.getUser());
}
測試結果:發現雖然已經將語句進行了分開查詢,但是並沒有延遲載入,還是一起查詢了。
原因:沒有開啟延遲載入的開關
開啟延遲載入的開關
延遲載入預設是false:
在全域性配置檔案中開啟延遲載入的開關:
<!-- 設定引數 -->
<settings>
<!--開啟駝峰-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--開啟延遲載入-->
<setting name="lazyLoadingEnabled" value="true"></setting>
</settings>
延遲載入分析圖例
注意:association標籤總的colunm屬性,如果傳遞的引數只有一個的話,直接使用user_id即可,如果是多個可以使用{user_id=id,user_nam=userName}來傳遞。
也可以修改如下:
附:
如果sql語句中出現’<’的解決方案
1、使用xml中的字元實體
【示例】
修改如下即可:
2、使用<![CDATA[ < ]]>
CDATA 內部的所有東西都會被解析器忽略,因此只要將字元實體編寫在其中,就不需要進行轉義。
如下:
附:Mybatis入門三天原始碼: