MyBatis 實(shí)踐
標(biāo)簽: Java與存儲(chǔ)
動(dòng)態(tài)SQL
動(dòng)態(tài)SQL提供了對(duì)SQL語句的靈活操作,通過表達(dá)式進(jìn)行判斷,對(duì)SQL進(jìn)行拼接/組裝.
if
對(duì)查詢條件進(jìn)行判斷,如果輸入?yún)?shù)不為空才進(jìn)行查詢條件的拼接.
<
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/>會(huì)自動(dòng)處理第一個(gè)AND(MyBatis還提供了自定義<where/>行為的<trim/>元素, 詳細(xì)可參考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, 因此這兩個(gè)條件不會(huì)拼接在SQL中,這一點(diǎn)可以調(diào)試時(shí)日志中看出.
choose/when/otherwise
有些時(shí)候,我們并不想用到所有的條件語句,而只想從中選擇一二.針對(duì)這種情況,MyBatis提供了<choose/>元素,他有點(diǎn)像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
用于動(dòng)態(tài)更新語句的解決方案為<set/>,set元素可以被用于動(dòng)態(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可以實(shí)現(xiàn)向SQL中傳遞數(shù)組或List:
傳入List
查詢多個(gè)id的用戶信息, 可以由下面兩種SQL實(shí)現(xiàn):
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解析的參數(shù)名 |
| index | 循環(huán)下標(biāo) |
| item | 單個(gè)元素的名 |
| open | 循環(huán)開始輸出 |
| close | 循環(huán)結(jié)束輸出 |
| separator | 中間分隔輸出 |
傳遞List作為parameterType時(shí),SQL解析參數(shù)名固定為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")));
}
傳入數(shù)組
<
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類似,傳遞數(shù)組作為parameterType時(shí),SQL解析參數(shù)名固定為array.
List<User> selectUser(
Integer[] ids) throws Exception;
SQL片段
可以將一段公共的SQL語句抽取出來, 作為一個(gè)SQL片段, 供其他SQL調(diào)用:
<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>
經(jīng)驗(yàn):最好基于單表定義SQL片段,而且在SQL片段中不要包含<where>/<set>之類的標(biāo)簽,這樣可以保證SQL片段重用度更高.
關(guān)聯(lián)查詢
數(shù)據(jù)模型分析思路
- 每張表的數(shù)據(jù)內(nèi)容:分模塊對(duì)每張表記錄的內(nèi)容進(jìn)行熟悉,相當(dāng)于學(xué)習(xí)系統(tǒng)需求/功能.
- 每張表重要的字段:非空字段/外鍵字段等.
- 表與表之間的數(shù)據(jù)庫級(jí)別關(guān)系: 外鍵關(guān)系.
- 表與表之間的業(yè)務(wù)關(guān)系:建立在某個(gè)業(yè)務(wù)的基礎(chǔ)上去分析.
訂單/商品數(shù)據(jù)模型
- 表內(nèi)容
- user: 購買商品的用戶信息
- order: 用戶創(chuàng)建的訂單
- orderdetail: 訂單詳細(xì)(購買商品信息)
- item: 商品信息
- 表與表之間的業(yè)務(wù)關(guān)系:
- user/order:
- user -> order: 一對(duì)多
- order -> user: 一對(duì)一
- order/orderdetail:
- order -> orderdetail:一對(duì)多
- orderdetail -> order:一對(duì)一
- orderdetail/item:
- orderdetail -> item:一對(duì)一
- item -> orderdetail:一對(duì)多
‘一對(duì)一’查詢
需求: 查詢訂單信息,關(guān)聯(lián)查詢(創(chuàng)建訂單的)用戶信息.
由以上分析可知主查詢?yōu)閛rder表,而order -> user關(guān)系為一對(duì)一, 因此使用resultMap將查詢結(jié)果的訂單信息映射到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: 映射關(guān)聯(lián)查詢的單條記錄(將關(guān)聯(lián)查詢信息映射到PO對(duì)象屬性).
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);}
}
‘一對(duì)多’查詢
需求: 查詢訂單及訂單明細(xì)的信息(一對(duì)多).
- PO: 定義OrderDetail,并在Order中添加List<OrderDetail> orderDetails訂單明細(xì)屬性:
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 | 指定關(guān)聯(lián)查詢的結(jié)果集存儲(chǔ)到的屬性 |
| ofType | 指定關(guān)聯(lián)查詢結(jié)果集中的對(duì)象類型 |
Order selectOrderWithDetail(
Integer id) throws Exception;
‘多對(duì)多’查詢
需求: 查詢用戶及用戶購買商品信息.
由于User表與Item表沒有直接關(guān)聯(lián),因此只能通過Order表與OrderDetail表進(jìn)行關(guān)聯(lián).
思路:
1) 將用戶信息映射到User中.
2) 在User中添加List<Order>訂單列表屬性,將用戶創(chuàng)建的訂單映射到orders.
3) 在Order中添加List<OrderDetail>訂單明細(xì)列表屬性,將訂單的明細(xì)映射到orderDetails.
4) 在OrderDetail中添加Item屬性,將訂單明細(xì)所對(duì)應(yīng)的商品映射到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小結(jié):
使用<association/>和<collection/>可以完成一對(duì)一和一對(duì)多的高級(jí)映射.
- association: 將關(guān)聯(lián)查詢信息映射到一個(gè)PO對(duì)象中.
- collection: 將關(guān)聯(lián)查詢信息映射到一個(gè)集合中.
延遲加載
關(guān)聯(lián)查詢時(shí),使用MyBatis 延遲加載 特性可有效減輕數(shù)據(jù)庫壓力.首次查詢只查詢主表信息,等需要時(shí)再去查詢關(guān)聯(lián)表信息.<resultMap/>的<association/>/<collection/>具備延遲加載功能.
需求: 查詢訂單信息并關(guān)聯(lián)查詢用戶信息.
延遲加載開關(guān)
在MyBatis核心配置文件(mybatis-configuration.xml)中配置:
1) lazyLoadingEnabled : 設(shè)置是否懶加載.默認(rèn)false,則所有關(guān)聯(lián)查詢都會(huì)被初始化加載.
2) aggressiveLazyLoading : 設(shè)置是否積極加載. 默認(rèn)true,所有關(guān)聯(lián)屬性被初始化加載.
Settings配置:
<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 | 指定關(guān)聯(lián)查詢Statement為com.fq.mybatis.UserDAO.selectUserById. |
| column | 指定關(guān)聯(lián)查詢時(shí)將users_id值傳入selectUserById. |
- 關(guān)聯(lián)查詢用戶信息(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來關(guān)聯(lián)查詢用戶信息.
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信息,會(huì)發(fā)現(xiàn)只有當(dāng)確實(shí)需要User信息時(shí)才會(huì)調(diào)用selectUserById.
轉(zhuǎn)載于:https://www.cnblogs.com/itrena/p/5926903.html
總結(jié)
以上是生活随笔為你收集整理的MyBatis 实践 -动态SQL/关联查询的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。