關于MyBatis中映射對象關系的舉例
MyBatis映射對象關系
雙向many2one/one2many關系中的組合關系(級聯(lián))
上面已經(jīng)看到了雙向many2one/one2many關系的映射。
但是我們說,在關聯(lián)關系中,還存在組合/聚合關系。
所謂聚合就是one方和many方可以獨立存在;組合關系是強聚合關系,one方和many方解除關系后,就沒有實際意義了。
在組合關系中,必須要處理的一個問題就是級聯(lián)關系。
下面使用SaleBill和SaleBillItem對象來完成雙向的many2one/one2many的組合關系。
對象設計如下:
//銷售單對象
public class SaleBill {
private Long id;
private String sn;
private Set<</span>SaleBillItem> items = new HashSet<</span>SaleBillItem>();
//getter & setter
}
//銷售單明細對象
public class SaleBillItem {
private Long id;
private Double price;
private SaleBill bill;
//getter & setter
}- 對于組合關系,數(shù)據(jù)表的結(jié)構設計和many2one一樣,都是many方一個外鍵引用one方的主鍵。
- 在組合關系中,一般來說,明細項是不需要額外有一個Mapper文件的,所有的映射都應該放在One方的映射文件中。
下面來看看映射文件:
<mapper namespace="cd.itcast.mybatis.salebill.SaleBillMapper">
<!-- 保存SaleBill對象,該調(diào)用必須放在SaleBillItem對象保存之前 -->
<insert id="saveSaleBill" keyProperty="id" parameterType="SaleBill"
useGeneratedKeys="true">
INSERT INTO salebill(sn) values (#{sn})
</insert>
<!-- 保存SaleBillItem對象 -->
<insert id="saveSaleBillItem" keyProperty="id" parameterType="SaleBillItem" useGeneratedKeys="true">
INSERT INTO salebillitem(price,bill_id) values (#{price},#{bill.id})
</insert>
<!-- 內(nèi)嵌映射SaleBill對象,注意,因為是組合關系,所以不會出現(xiàn)單獨去拿SaleBillItem對象的情況,所以只需要映射SaleBill主對象即可 -->
<resultMap type="SaleBill" id="salebillmap">
<id property="id" column="id"/>
<result column="sn" property="sn"/>
<collection property="items" column="id" ofType="SaleBillItem" >
<id column="item_id" property="id"/>
<result column="item_price" property="price"/>
<association property="bill" column="bill_id" resultMap="salebillmap" />
</collection>
</resultMap>
<!-- 因為列表和單獨的get方法都是采用內(nèi)聯(lián)的查詢,所以把公用的SQL提取出來 -->
<sql id="select">
SELECT b.*,item.id as item_id,item.price as item_price FROM salebill b LEFT JOIN salebillitem item ON b.id = item.bill_id
</sql>
<!-- get -->
<select id="get" parameterType="long" resultMap="salebillmap">
<include refid="select"/> WHERE b.id = #{id}
</select>
<!-- 列表查詢 -->
<select id="list" resultMap="salebillmap">
<include refid="select"/>
</select>
<!-- 刪除SaleBill對象 -->
<delete id="delete" parameterType="long">
DELETE FROM salebill WHERE id = #{id}
</delete>
<!-- 刪除SaleBill對象對應的Item對象,注意,因為有外鍵的約束,這條刪除語句應該放在delete SaleBill之前執(zhí)行。注意,傳入的這個id應該是SaleBill的id -->
<delete id="deleteItems" parameterType="long">
DELETE FROM salebillitem WHERE bill_id = #{id}
</delete>
</mapper>映射文件的內(nèi)容都已經(jīng)注釋詳細。
主要注意一點的就是在設計映射文件的時候需要考慮組合關系的一般設計方式,所以在上面的配置文件中只做了SaleBill對象的映射。
這個配置文件還是很好理解的,其中所有的配置點在前面都已經(jīng)介紹過。
在組合關系中,主要的問題就是一個級聯(lián)保存,但是注意,在mybatis中設計組合的級聯(lián)保存,刪除,這個過程應該是我們自己來控制的。
下面來看看怎么測試組合關系:
//先創(chuàng)建一個Mapper接口,使用接口來完成映射
public interface SaleBillMapper {
? ? void saveSaleBill(SaleBill sb);
? ? void saveSaleBillItem(SaleBillItem sbi);
? ? SaleBill get(Long id);
? ? //在接口中可以直接映射分頁對象
? ? List<</span>SaleBill> list(RowBounds rb);
? ? void delete(Long id);
? ? void deleteItems(Long billId);
}具體測試代碼:
@Test
public void testSave() {
? ? //創(chuàng)建SaleBill對象
? ? SaleBill sb = new SaleBill();
? ? sb.setSn("001");
? ? //創(chuàng)建一個明細對象
? ? SaleBillItem sbi = new SaleBillItem();
? ? sbi.setPrice(100d);
? ? sbi.setBill(sb);
? ? //創(chuàng)建一個明細對象
? ? SaleBillItem sbi2 = new SaleBillItem();
? ? sbi2.setPrice(300d);
? ? sbi2.setBill(sb);
? ? //處理關系
? ? sb.getItems().add(sbi);
? ? sb.getItems().add(sbi2);
? ? //保存
? ? SqlSession session = MyBatisUtil.openSession();
? ? SaleBillMapper mapper = session.getMapper(SaleBillMapper.class);
? ? //注意,先保存SaleBill對象
? ? mapper.saveSaleBill(sb);
? ? //保存明細對象
? ? mapper.saveSaleBillItem(sbi);
? ? mapper.saveSaleBillItem(sbi2);
? ? session.commit();
? ? session.close();
}
?
@Test
public void testGet() {
? ? SqlSession session = MyBatisUtil.openSession();
? ? //得到SaleBill對象
? ? SaleBillMapper mapper = session.getMapper(SaleBillMapper.class);
? ? SaleBill sb = mapper.get(5l);
? ? Set<</span>SaleBillItem> items = sb.getItems();
? ? for (SaleBillItem item : items) {
? ? ? ? ? ? System.out.println(item.getPrice());
? ? }
? ? session.close();
}
?
@Test
public void testDelete() {
? ? SqlSession session = MyBatisUtil.openSession();
? ? SaleBillMapper mapper = session.getMapper(SaleBillMapper.class);
? ? mapper.deleteItems(1l);
? ? mapper.delete(1l);
? ? session.commit();
? ? session.close();
} ? ?Mybatis映射原理
MyBatis的真正強大之處在于它的映射語句,這也是它的魔力所在。由于它的映射語句異常強大,映射器的 XML 文件就顯得相對簡單。
MyBatis3.0相比2.0版本的一個最大變化,就是支持使用接口來調(diào)用方法。
- 以前使用 SqlSession 通過命名空間調(diào)用 MyBatis 方法時,首先需要用到命名空間和方法id 組成的字符串來調(diào)用相應的方法 。
- 當參數(shù)多于 1 個的時候,需要將所有參數(shù)放到一個 Map對象中 。 通過 Map 傳遞多個參數(shù),使用起來很不方便,而且還無法避免很多重復的代碼。
- 使用接口調(diào)用方式就會方便很多, MyBatis 使用 Java 的動態(tài)代理可以直接通過接口來調(diào)用 相應 的方法,不需要提供接口的實現(xiàn)類,更不需要在實現(xiàn)類中使用 SqlSess 工∞以通過命名空 間間接調(diào)用 。
- 另外,當有多個參數(shù)的時候,通過參數(shù)注解@ Par am 設置參數(shù)的名字省去了 手動構造 Map 參數(shù)的過程,尤其在 Spring 中使用的時候,可以配置為自動掃描所有的接口類 ,直接將接口注入需要用到的地方。
mapper文件示例:
<?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.cnpiec.ireader.dao.GetBookDataDao">
? ? <insert id="batchInsertBook" parameterType="Book">
? ? ? ? INSERT INTO BOOK (BOOKID,NAME,PARTNERNAME) VALUES
? ? ? ? <foreach collection="list" item="book" separator=",">
? ? ? ? ? ? (#{book.bookId}, #{book.name}, #{book.partnerName})
? ? ? ? </foreach>
? ? </insert>
</mapper>需要注意的是<mapper>根標簽的 name space 屬性。當 Mapper 接口和 XML 文件關聯(lián)的時候,命名空間口amespace 的值就需要配置成接口的全限定名稱,例如 UserMapper 接口對應的 tk. mybatis . simple .mapper . UserMapper, MyBatis 內(nèi)部就是通過這個值將接口和XML 關聯(lián)起來的。
mybati s-config.xml 配置文件中的 mappers 元素中配置所有的 mapper ,部分配置代碼如下 。
<mappers> ? ? <mapper resource=” tk/mybatis/simple/mapper/CountryMapper.xml ” / > ? ? <mapper resource=” tk/mybatis/simple/mapper/UserMapper . xml ” / > ? ? <mapper resource=” tk/mybatis/simple/mapper/RoleMapper.xml ” /> ? ? <mapper resource=” tk/mybatis/simple/mapper/PrivilegeMapper . xml ” /> ? ? <mapper resource=” tk/mybatis/simple/mapper/UserRoleMapper . xml ” /> ? ? <mapper resource=” tk/mybatis/simple/mapper / RolePrivilegeMapper.xml ” /> </mappers>
更簡單的配置方式,代碼如下
<mappers> ? ? <package name= ” tk.mybatis . simple . mapper ” /> </mappers>
這種配置方式會先查找 tk.mybatis.simple . mapper 包下所有的接口,循環(huán)對接口進行如下操作。這種配置方式會先查找 tk.mybatis.simple . mapper 包下所有的接口,循環(huán)對接口進行判斷接口對應的命名 空 間是否己經(jīng)存在,如果不存在就拋出異常,存在就繼續(xù)進行接下來的操作。加載接口對應的卻也映射文件 , 將接口全限定名轉(zhuǎn)換為路徑.
為什么 Mapper 接口沒有實現(xiàn)類卻能被正常調(diào)用呢?
這是因為MyBaits 在 Mapper 接口上使用了動態(tài)代理的一種非常規(guī)的用法,熟悉這動態(tài)代理的用法不僅有利于理解 MyBatis 接口和 XML 的關系,還能開闊思路 。
從代理類中可以看到,當調(diào)用 一個接口的方法時,會先通過接口的全限定名稱和當前調(diào)用的方法名的組合得到一個方法 id,這個 id 的值就是映射 XML 中口arnespa ce 和具體方法 id的組合。所以可以在代理方法中使用 sqlSession 以命名空間的方式調(diào)用方法。通過這種方式可以將接口和 XML 文件中的方法關聯(lián)起來。這種代理方式和常規(guī)代理的不同之處在于,這里沒有對某個具體類進行代理,而是通過代理轉(zhuǎn)化成了對其他代碼的調(diào)用。
由于方法參數(shù)和返回值存在很多種情況,因此 MyBatis 的內(nèi)部實現(xiàn)會比上面的邏輯復雜得多,正是因為 MyBatis 對接口動態(tài)代理的實現(xiàn),我們在使用接口方式的時候才會如此容易。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
使用Spring自定義實現(xiàn)IOC和依賴注入(注解方式)
這篇文章主要介紹了使用Spring自定義實現(xiàn)IOC和依賴注入(注解方式),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
net.sf.json.JSONObject 為null 的判斷方法
下面小編就為大家?guī)硪黄猲et.sf.json.JSONObject 為null 的判斷方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-02-02

