MyBatis 實踐
標簽: Java與存儲
動態(tài)SQL
動態(tài)SQL提供了對SQL語句的靈活操作,通過表達式進行判斷,對SQL進行拼接/組裝.
if
對查詢條件進行判斷,如果輸入參數不為空才進行查詢條件的拼接.
<
select id=
"selectUser" resultType=
"com.fq.domain.User" parameterType=
"com.fq.domain.User">
SELECT *
FROM user<
where><
if test=
"id != null">
AND id =
#{id}</
if><
if test=
"name != null">
AND name =
#{name}</
if><
if test=
"password != null">
AND password =
#{password}</
if></
where>
</
select>
<where/>會自動處理第一個AND(MyBatis還提供了自定義<where/>行為的<trim/>元素, 詳細可參考MyBatis文檔).
List<User> selectUser(User user) throws
Exception;
@Test
public void selectUserClient()
throws Exception {UserDAO dao = session.getMapper(UserDAO.class);User user = dao.selectUser(
new User(
null,
null,
"new_password"));System.out.println(user);
}
由于id與name為null, 因此這兩個條件不會拼接在SQL中,這一點可以調試時日志中看出.
choose/when/otherwise
有些時候,我們并不想用到所有的條件語句,而只想從中選擇一二.針對這種情況,MyBatis提供了<choose/>元素,他有點像Java中的switch.
<
select id=
"selectUser" resultType=
"com.fq.domain.User" parameterType=
"com.fq.domain.User">
SELECT *
FROM user<
where><choose><
when test=
"id != null">
AND id =
#{id}</
when><
when test=
"name != null">
AND name =
#{name}</
when><otherwise>
AND password =
#{password}</otherwise></choose></
where>
</
select>
set
用于動態(tài)更新語句的解決方案為<set/>,set元素可以被用于動態(tài)包含需要更新的列, 而舍去其他的.
<update
id=
"updateUserById" parameterType=
"com.fq.domain.User">UPDATE user<
set><
if test=
"name != null">
name = </
if><
if test=
"password != null">password = </
if></
set>WHERE
id =
</update>
foreach
使用foreach可以實現向SQL中傳遞數組或List:
傳入List
查詢多個id的用戶信息, 可以由下面兩種SQL實現:
SELECT * FROM user WHERE (id = ? OR id = ? OR id = ?);
SELECT * FROM user WHERE id IN (?, ?, ?, ?); 因此其foreach的定義也有如下兩種方案:
<
select id=
"selectUser" parameterType=
"java.util.List" resultType=
"com.fq.domain.User">SELECT *FROM user<
where><
if test=
"list != null and list.size >= 1"><
foreach collection=
"list" item=
"id" open=
"(" separator=
"or" close=
")">id =
#{id}</
foreach></
if></
where>
</
select> <
select id=
"selectUser" parameterType=
"java.util.List" resultType=
"com.fq.domain.User">SELECT *FROM user<
where><
if test=
"list != null and list.size >= 1"><
foreach collection=
"list" item=
"id" open=
"id IN (" separator=
"," close=
")">
#{id}</
foreach></
if></
where>
</
select>
元素描述
| collection | SQL解析的參數名 |
| index | 循環(huán)下標 |
| item | 單個元素的名 |
| open | 循環(huán)開始輸出 |
| close | 循環(huán)結束輸出 |
| separator | 中間分隔輸出 |
傳遞List作為parameterType時,SQL解析參數名固定為list.
List<User> selectUser(
List<
Integer> ids) throws Exception;
批量插入用戶案例
<insert id="insertUserList" parameterType="java.util.List">INSERT INTO user(name, password) VALUES
<if test="list != null and list.size != 0"><foreach collection="list" item="user" separator=",">(#{user.name}, #{user.password})
</foreach></if>
</insert> void insertUserList(List<User> users)
throws Exception;
@Test
public void insertUserListClient()
throws Exception {UserDAO dao = session.getMapper(UserDAO.class);dao.insertUserList(Lists.newArrayList(
new User(
null,
"mojia5",
"mojia5"),
new User(
null,
"mojia6",
"mojia6"),
new User(
null,
"mojia7",
"mojia7")));
}
傳入數組
<
select id=
"selectUser" parameterType=
"Object[]" resultType=
"com.fq.domain.User">SELECT *FROM user<
where><
if test=
"array != null and array.length >= 1"><
foreach collection=
"array" item=
"id" open=
"id IN (" separator=
"," close=
")">
#{id}</
foreach></
if></
where>
</
select>
與List類似,傳遞數組作為parameterType時,SQL解析參數名固定為array.
List<User> selectUser(
Integer[] ids) throws Exception;
SQL片段
可以將一段公共的SQL語句抽取出來, 作為一個SQL片段, 供其他SQL調用:
<sql id=
"user_where"><
if test=
"id != null">
AND id =
#{id}</
if><
if test=
"name != null">
AND name =
#{name}</
if><
if test=
"password != null">
AND password =
#{password}</
if>
</sql><
select id=
"selectUser" resultType=
"com.fq.domain.User" parameterType=
"com.fq.domain.User">
SELECT *
FROM user<
where><include refid=
"user_where"/></
where>
</
select>
經驗:最好基于單表定義SQL片段,而且在SQL片段中不要包含<where>/<set>之類的標簽,這樣可以保證SQL片段重用度更高.
關聯查詢
數據模型分析思路
- 每張表的數據內容:分模塊對每張表記錄的內容進行熟悉,相當于學習系統需求/功能.
- 每張表重要的字段:非空字段/外鍵字段等.
- 表與表之間的數據庫級別關系: 外鍵關系.
- 表與表之間的業(yè)務關系:建立在某個業(yè)務的基礎上去分析.
訂單/商品數據模型
- 表內容
- user: 購買商品的用戶信息
- order: 用戶創(chuàng)建的訂單
- orderdetail: 訂單詳細(購買商品信息)
- item: 商品信息
- 表與表之間的業(yè)務關系:
- user/order:
- user -> order: 一對多
- order -> user: 一對一
- order/orderdetail:
- order -> orderdetail:一對多
- orderdetail -> order:一對一
- orderdetail/item:
- orderdetail -> item:一對一
- item -> orderdetail:一對多
‘一對一’查詢
需求: 查詢訂單信息,關聯查詢(創(chuàng)建訂單的)用戶信息.
由以上分析可知主查詢?yōu)閛rder表,而order -> user關系為一對一, 因此使用resultMap將查詢結果的訂單信息映射到Order中,將用戶信息映射到Order中的User屬性.
public class User implements Serializable {private Integer id;
private String username;
private Date birthday;
private Integer sex;
private String address;
}
- PO: 新增Order, 將User組合到Order中:
public class Order implements Serializable {private Integer id;
private Integer userId;
private String number;
private Date createTime;
private String note;
private User user;
}
<?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="com.fq.mybatis.OrderDAO"><cache type="org.mybatis.caches.ehcache.EhcacheCache"/><resultMap id="order_user_map" type="com.fq.domain.Order"><id column="id" property="id"/><result column="user_id" property="userId"/><result column="number" property="number"/><result column="create_time" property="createTime"/><result column="note" property="note"/><association property="user" javaType="com.fq.domain.User"><id column="user_id" property="id"/><result column="username" property="username"/><result column="birthday" property="birthday"/><result column="sex" property="sex"/><result column="address" property="address"/></association></resultMap><select id="selectOrderWithUser" resultMap="order_user_map">SELECT`order`.*,username,birthday,sex,addressFROM `order`, userWHERE `order`.user_id = user.id AND `order`.id = #{0};
</select></mapper> association: 映射關聯查詢的單條記錄(將關聯查詢信息映射到PO對象屬性).
public interface OrderDAO {Order selectOrderWithUser(Integer id) throws Exception;
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations =
"classpath:spring/applicationContext.xml")
public class OrderDAOClient {@Autowiredprivate OrderDAO dao;
@Testpublic void client()
throws Exception {Order order = dao.selectOrderWithUser(
3);System.out.println(order);}
}
‘一對多’查詢
需求: 查詢訂單及訂單明細的信息(一對多).
- PO: 定義OrderDetail,并在Order中添加List<OrderDetail> orderDetails訂單明細屬性:
public class OrderDetail implements Serializable {private Integer id;
private Integer orderId;
private Integer itemId;
private Integer itemNumber;
}
<resultMap id=
"order_user_detail_map" type=
"com.fq.domain.Order" extends=
"order_user_map"><collection
property=
"orderDetails" ofType=
"com.fq.domain.OrderDetail"><id column=
"order_detail_id" property=
"id"/><result column=
"item_id" property=
"itemId"/><result column=
"item_num" property=
"itemNumber"/><result column=
"order_id" property=
"orderId"/></collection>
</resultMap><
select id=
"selectOrderWithDetail" resultMap=
"order_user_detail_map">
SELECT`
order`.*,username,birthday,sex,address,orderdetail.id order_detail_id,item_id,item_num,order_id
FROM `
order`, user, orderdetail
WHERE `
order`.user_id = user.id
AND `
order`.id = orderdetail.order_id
AND `
order`.id =
#{0};
</
select>
元素描述
| property | 指定關聯查詢的結果集存儲到的屬性 |
| ofType | 指定關聯查詢結果集中的對象類型 |
Order selectOrderWithDetail(
Integer id) throws Exception;
‘多對多’查詢
需求: 查詢用戶及用戶購買商品信息.
由于User表與Item表沒有直接關聯,因此只能通過Order表與OrderDetail表進行關聯.
思路:
1) 將用戶信息映射到User中.
2) 在User中添加List<Order>訂單列表屬性,將用戶創(chuàng)建的訂單映射到orders.
3) 在Order中添加List<OrderDetail>訂單明細列表屬性,將訂單的明細映射到orderDetails.
4) 在OrderDetail中添加Item屬性,將訂單明細所對應的商品映射到item.
PO: Item
public class Item {
private Integer id;
private String name;
private Float price;
private String detail;
private String pic;
private Date createTime;
}
<resultMap
id=
"user_item_map" type=
"com.fq.domain.User"><
id column=
"id" property=
"id"/><
result column=
"username" property=
"username"/><
result column=
"birthday" property=
"birthday"/><
result column=
"sex" property=
"sex"/><
result column=
"address" property=
"address"/><collection
property=
"orders" ofType=
"com.fq.domain.Order"><
id column=
"order_id" property=
"id"/><
result column=
"id" property=
"userId"/><
result column=
"order_create_time" property=
"createTime"/><
result column=
"order_note" property=
"note"/><
result column=
"order_number" property=
"number"/><collection
property=
"orderDetails" ofType=
"com.fq.domain.OrderDetail"><
id column=
"order_detail_id" property=
"id"/><
result column=
"order_id" property=
"orderId"/><
result column=
"item_id" property=
"itemId"/><
result column=
"order_item_num" property=
"itemNumber"/><association
property=
"item" javaType=
"com.fq.domain.Item"><
id column=
"item_id" property=
"id"/><
result column=
"item_create_time" property=
"createTime"/><
result column=
"item_detail" property=
"detail"/><
result column=
"item_name" property=
"name"/><
result column=
"item_price" property=
"price"/><
result column=
"item_pic" property=
"pic"/></association></collection></collection>
</resultMap><select
id=
"selectUserItem" resultMap=
"user_item_map">SELECTuser.*,`order`.
id order_id,`order`.create_time order_create_time,`order`.note order_note,`order`.
number order_number,orderdetail.
id order_detail_id,orderdetail.item_num order_item_num,
item.
id item_id,
item.create_time item_create_time,
item.detail item_detail,
item.
name item_name,
item.price item_price,
item.pic item_picFROM user,
item, `order`, orderdetailWHERE `order`.user_id = user.
id AND orderdetail.order_id = `order`.
id AND orderdetail.item_id =
item.
id ;
</select>
List<User> selectUserItem() throws
Exception;
resultMap小結:
使用<association/>和<collection/>可以完成一對一和一對多的高級映射.
- association: 將關聯查詢信息映射到一個PO對象中.
- collection: 將關聯查詢信息映射到一個集合中.
延遲加載
關聯查詢時,使用MyBatis 延遲加載 特性可有效減輕數據庫壓力.首次查詢只查詢主表信息,等需要時再去查詢關聯表信息.<resultMap/>的<association/>/<collection/>具備延遲加載功能.
需求: 查詢訂單信息并關聯查詢用戶信息.
延遲加載開關
<settings><setting name="cacheEnabled" value="true"/><setting name="lazyLoadingEnabled" value="true"/><setting name="aggressiveLazyLoading" value="false"/>
</settings> Mapper
<resultMap
id=
"order_user_map" type=
"com.fq.domain.Order"><
id column=
"id" property=
"id"/><
result column=
"user_id" property=
"userId"/><
result column=
"number" property=
"number"/><
result column=
"create_time" property=
"createTime"/><
result column=
"note" property=
"note"/><association
property=
"user" javaType=
"com.fq.domain.User" select=
"com.fq.mybatis.UserDAO.selectUserById" column=
"user_id"/>
</resultMap><select
id=
"selectOrderWithUser" resultMap=
"order_user_map">SELECT *FROM `order`;
</select>
元素描述
| select | 指定關聯查詢Statement為com.fq.mybatis.UserDAO.selectUserById. |
| column | 指定關聯查詢時將users_id值傳入selectUserById. |
- 關聯查詢用戶信息(namespace為com.fq.mybatis.UserDAO)
<
select id=
"selectUserById" parameterType=
"java.lang.Integer" resultType=
"com.fq.domain.User">
SELECT *
FROM user
WHERE id =
#{id};
</
select>
將上面查詢到的訂單信息中的user_id傳入selectUserById來關聯查詢用戶信息.
List<
Order> selectOrderWithUser() throws Exception;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations =
"classpath:spring/applicationContext.xml")
public class OrderDAOClient {@Autowiredprivate OrderDAO dao;
@Testpublic void client()
throws Exception {List<Order> orders = dao.selectOrderWithUser();
for (Order order : orders) {System.out.println(order.getUser());}}
}
debug上面Client, 觀察log信息,會發(fā)現只有當確實需要User信息時才會調用selectUserById.
轉載于:https://www.cnblogs.com/itrena/p/5926903.html
總結
以上是生活随笔為你收集整理的MyBatis 实践 -动态SQL/关联查询的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。