前面幾節的示例基本都是一些單表查詢,實際項目中,經常用到關聯表的查詢,比如一對一,一對多等情況。在Java實體對象中,一對一和一對多可是使用包裝對象解決,屬性使用List或者Set來實現,在mybatis中一對一和一對多可是使用association或者collection標簽來配合實現。
在MyBatis中有兩種方式實現關聯查詢:
1. 嵌套結果:使用嵌套結果映射來處理重復的聯合結果的子集。首先,然讓我們來查看這個元素的屬性。所有的你都會看到,它和普通的只由 select 和resultMap 屬性的結果映射不同
2. 嵌套查詢:通過執行另外一個 SQL 映射語句來返回預期的復雜類型
一對一查詢
示例:商品表products 和 商品分類category 一個商品對應一種分類,所以products->category是一對一的關系
1.嵌套結果實現(resultMap):
通過resultMap將查詢結果中商品信息映射到Product對象中,在Product類中添加屬性category,將關聯結果映射到Product.category屬性上。
<!-- 一對一查詢 關聯查詢 使用resultMap映射結果集 --> <select id="oneToOneTestMap" parameterType="int" resultMap="productInfoMap"> select p.*,c.id categoryId,c.name categoryName,c.remark categoryRemark from products p join category c on p.categoryId= c.Id where p.id =#{value} </select> <resultMap id="productInfoMap" type="com.sl.po.Product"> <id column="id" property="Id" /> <result column="name" property="Name" /> <result column="description" property="Description" /> <result column="unitprice" property="UnitPrice" /> <result column="imageUrl" property="ImageUrl" /> <result column="isnew" property="IsNew" /> <result column="citycode" property="cityCode" /> <!-- association:用於映射關聯查詢單個對象的信息 property:要將關聯查詢的分類信息映射到屬性category上 --> <association property="category" javaType="com.sl.po.Category"> <id column="categoryId" property="Id" /> <result column="categoryName" property="Name" /> <result column="categoryRemark" property="Remark" /> </association> </resultMap>
注:也可以使用resultType配置實現,需要定義一個包裝類含有product和category兩個對象的屬性即可。
2.嵌套查詢實現
<!-- 關聯嵌套查詢 --> <select id="oneToOneTestAssociationSelect" parameterType="int" resultMap="productInfoMap2"> select p.* from products p where p.id =#{value} </select> <resultMap id="productInfoMap2" type="com.sl.po.Product" > <id column="id" property="Id" /> <result column="name" property="Name" /> <result column="description" property="Description" /> <result column="unitprice" property="UnitPrice" /> <result column="imageUrl" property="ImageUrl" /> <result column="isnew" property="IsNew" /> <result column="citycode" property="cityCode" /> <!-- column:傳遞子查詢參數,如果要處理符復合主鍵,使用column= "{prop1=col1,prop2=col2}" --> <!-- select:子查詢語句id --> <association property="category" javaType="com.sl.po.Category" column="categoryId" select="selectCategoryInfo"> </association> </resultMap> <!-- 子查詢 --> <select id="selectCategoryInfo" parameterType="int" resultType="com.sl.po.Category"> select * from category where id=#{id} </select>
使用resultType、resultMap和嵌套查詢對比分析:
resultType:使用resultType只需要將查詢結果集中的列名與定義的pojo屬性一一對應即可完成映射,缺點:resultType無法將查詢結果映射到包裝類的pojo屬性中
resultMap:需要額外定義resultMap,在resultMap中將結果集列名與pojo對象屬性一一配置。
嵌套查詢:需要定義多個查詢,上面示例定義兩個查詢,一個用於查詢products表,一個用於查詢category表。由於使用的是嵌套查詢,當操作大型數據集合和列表時將會帶來頻繁操作數據庫問題。即執行一條sql獲取結果集(oneToOneTestAssociationSelect),根據該結果集,循環執行嵌套查詢獲取具體信息(selectCategoryInfo),與上面的嵌套結果查詢相比,這種情況顯然有明顯不足。
3. 測試代碼
定義Product和Category實體,添加Mapper接口

package com.sl.po; import java.math.BigDecimal; public class Product { private int Id; private String Name; private String Description; private BigDecimal UnitPrice; private String ImageUrl; private Boolean IsNew; private String cityCode; private int categoryId; private Category category; public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } public int getCategoryId() { return categoryId; } public void setCategoryId(int categoryId) { this.categoryId = categoryId; } public String getCityCode() { return cityCode; } public void setCityCode(String cityCode) { this.cityCode = cityCode; } public int getId() { return Id; } public void setId(int id) { this.Id = id; } public String getName() { return Name; } public void setName(String name) { this.Name = name; } public String getDescription() { return Description; } public void setDescription(String description) { this.Description = description; } public BigDecimal getUnitPrice() { return UnitPrice; } public void setUnitPrice(BigDecimal unitprice) { this.UnitPrice = unitprice; } public String getImageUrl() { return Name; } public void setImageUrl(String imageurl) { this.ImageUrl = imageurl; } public boolean getIsNew() { return IsNew; } public void setIsNew(boolean isnew) { this.IsNew = isnew; } @Override public String toString() { return "Product [Id=" + Id + ", Name=" + Name + ", Description=" + Description + ", UnitPrice=" + UnitPrice + ", ImageUrl=" + ImageUrl + ", IsNew=" + IsNew + ", cityCode=" + cityCode + ", categoryId=" + categoryId + ", category=" + category + "]"; } }

package com.sl.po; import java.util.List; public class Category { private int Id; private String Name; private String Remark; private List<Product> productList; public int getId() { return Id; } public List<Product> getProductList() { return productList; } public void setProductList(List<Product> productList) { this.productList = productList; } public void setId(int id) { Id = id; } public String getName() { return Name; } public void setName(String name) { Name = name; } public String getRemark() { return Remark; } public void setRemark(String remark) { Remark = remark; } @Override public String toString() { return "Category [Id=" + Id + ", Name=" + Name + ", Remark=" + Remark + ", productList=" + productList + "]"; } }


// 一對一 使用resultType映射結果集 // @Test public void testSelectProduct() { // 獲取mapper接口的代理對象 UnitMapper unitMapper = session.getMapper(UnitMapper.class); ProductDetailInfo detailInfo = unitMapper.oneToOneTest(1); System.out.println(detailInfo); // 關閉會話 session.close(); } // 一對一 使用resultMap映射結果集 // @Test public void testSelectProduct2() { // 獲取mapper接口的代理對象 UnitMapper unitMapper = session.getMapper(UnitMapper.class); Product product = unitMapper.oneToOneTestMap(2); System.out.println(product); System.out.println(product.getCategory().toString()); // 關閉會話 session.close(); } //嵌套查詢 //一對一 //@Test public void testSelectProductTest() { // 獲取mapper接口的代理對象 UnitMapper unitMapper = session.getMapper(UnitMapper.class); Product product = unitMapper.oneToOneTestAssociationSelect(2); System.out.println(product); System.out.println(product.getCategory().toString()); // 關閉會話 session.close(); }
一對多查詢
將上面一對一的示例倒過來看,一種類別下有多個商品,所以category->products是一對多的關系
mybatis中可以通過使用resultMap的collection標簽將關聯查詢的多條記錄映射到一個list集合屬性中。
1.嵌套結果實現
<!-- 一對多映射 --> <select id="oneToManyTest" resultMap="categoryInfo"> select c.id cid,c.`name` cname,c.remark, p.* from category c join products p on p.categoryId= c.Id where c.id= #{cid} </select> <resultMap type="com.sl.po.Category" id="categoryInfo"> <id column="cid" property="id" /> <result column="cname" property="name" /> <result column="remark" property="remark" /> <!-- collection標簽,一對多映射,關聯當前分類下產品信息 property映射集合結果,ofType結果集類型 --> <collection property="productList" ofType="com.sl.po.Product"> <id property="id" column="id" javaType="int" jdbcType="INTEGER" /> <result column="name" property="Name" /> <result column="description" property="Description" /> <result column="unitprice" property="UnitPrice" /> <result column="imageUrl" property="ImageUrl" /> <result column="isnew" property="IsNew" /> <result column="citycode" property="cityCode" /> </collection> </resultMap>
2.嵌套查詢實現
<!-- 集合嵌套查詢 --> <select id="oneToManyTestCollectionSelect" resultMap="categoryInfo2"> select * from category c where c.id= #{id} </select> <resultMap id="categoryInfo2" type="com.sl.po.Category"> <id column="id" property="id" /> <result column="name" property="name" /> <result column="remark" property="remark" /> <!--collection 映射一對多結果集 column:傳遞嵌套查詢參數 select:嵌套查詢id--> <collection property="productList" ofType="com.sl.po.Product" column="id" select="selectProductByCategoryId"> </collection> </resultMap> <!-- 嵌套子查詢 --> <select id="selectProductByCategoryId" resultType="com.sl.po.Product"> select * from products where categoryId= #{id} </select>
測試代碼:
定義Product和Category實體同上,添加Mapper接口

public interface UnitMapper { Category oneToManyTest(int cId); Product oneToOneTestAssociationSelect(int id); //嵌套查詢中的子查詢也需要定義接口 Category selectCategoryInfo(int id); }

//一對多 @Test public void oneToManyTest() { UnitMapper unitMapper = session.getMapper(UnitMapper.class); Category catrgoryInfo = unitMapper.oneToManyTest(1); System.out.println(catrgoryInfo); if (catrgoryInfo.getProductList().size() > 0) { for (Product pro : catrgoryInfo.getProductList()) { System.out.println(pro); } } // 關閉會話 session.close(); } //嵌套查詢 一對多 @Test public void testoneToManyTestCollectionSelect() { // 獲取mapper接口的代理對象 UnitMapper unitMapper = session.getMapper(UnitMapper.class); Category category = unitMapper.oneToManyTestCollectionSelect(1); System.out.println(category); if (category.getProductList().size() > 0) { for (Product pro : category.getProductList()) { System.out.println(pro); } } // 關閉會話 session.close(); }
多對多查詢
示例:一個訂單包含多個商品,一個商品也可以對應多個訂單,這個示例查詢稍微擴展一下,增加用戶信息,一個用戶對應多個訂單
實體對象定義:定義User實體,增加orders屬性,用於映射當前用戶的訂單; 定義Order對象,增加orderItems屬性,映射當前訂單有哪些內容(產品);定義OrderItem實體,增加product屬性,映射具體產品信息
<!-- 多對多映射 查詢用戶信息及對應訂單信息,訂單詳情 --> <!-- select * from orders o join `user` u on o.userId = u.id join orderItem i on o.Id = i.orderid join products p on i.productid = p.Id --> <select id="manyToManyTest" resultMap="userAndOrderInfo"> select u.*, o.id oid, o.createtime ocreatetime, o.userid ouserid, o.amount oamount, o.remark oremark, i.id iid, i.orderid iorderid, i.productid iproductid, i.createtime icreatetime, i.number inumber, i.price iprice, p.id pid, p.`name` pname, p.Description pdescription from orders o join `user` u on o.userId = u.id join orderItem i on o.Id = i.orderid join products p on i.productid = p.Id where u.id=#{id} </select> <resultMap type="com.sl.po.User" id="userAndOrderInfo"> <id column="id" property="id" /> <result column="sex" property="sex" /> <result column="birthday" property="birthday" /> <result column="address" property="address" /> <result column="username" property="userName" /> <!-- 映射用戶對應的訂單信息,一個用戶可以有多個訂單 --> <collection property="orders" ofType="com.sl.po.Order"> <id column="oid" property="id" /> <result column="ocreatetime" property="createTime" /> <result column="ouserid" property="userId" /> <result column="oamount" property="amount" /> <result column="oremark" property="remark" /> <!-- 訂單對應的商品信息,一個訂單可以有多個商品 --> <collection property="orderItems" ofType="com.sl.po.OrderItem"> <id column="iid" property="id" /> <result column="iorderid" property="orderId" /> <result column="iproductid" property="productId" /> <result column="icreatetime" property="createTime" /> <result column="inumber" property="number" /> <result column="iprice" property="price" /> <!-- 映射商品信息 (OrderItem與商品product 一一對應)--> <association property="product" javaType="com.sl.po.Product"> <id column="pid" property="Id" /> <result column="pname" property="Name" /> <result column="pdescription" property="Description" /> </association> </collection> </collection> </resultMap>
測試代碼

package com.sl.po; import java.sql.Date; import java.util.List; public class User { private int id; private String sex; private Date birthday; private String address; private String userName; //映射當前用戶訂單列表 private List<Order> orders; public List<Order> getOrders() { return orders; } public void setOrders(List<Order> orders) { this.orders = orders; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } @Override public String toString() { return "User [id=" + id + ", sex=" + sex + ", birthday=" + birthday + ", address=" + address + ", userName=" + userName + ", orders=" + orders + "]"; } }

package com.sl.po; import java.sql.Date; import java.util.List; public class Order { private int id; private int userId; private Date createTime; private int amount; private String remark; private User user; //映射訂單內容(產品信息) private List<OrderItem> orderItems; public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public int getAmount() { return amount; } public void setAmount(int amount) { this.amount = amount; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List<OrderItem> getOrderItems() { return orderItems; } public void setOrderItems(List<OrderItem> orderItems) { this.orderItems = orderItems; } }

package com.sl.po; public class OrderItem { private int id; private int orderId; private int productId; private int createTime; private int number; private int price; //訂單項對應的具體產品 private Product product; public Product getProduct() { return product; } public void setProduct(Product product) { this.product = product; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getOrderId() { return orderId; } public void setOrderId(int orderId) { this.orderId = orderId; } public int getProductId() { return productId; } public void setProductId(int productId) { this.productId = productId; } public int getCreateTime() { return createTime; } public void setCreateTime(int createTime) { this.createTime = createTime; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } }

package com.sl.mapper; import java.util.List; import com.sl.po.Category; import com.sl.po.Product; import com.sl.po.ProductDetailInfo; import com.sl.po.User; public interface UnitMapper { User manyToManyTest(int id); }

//多對多 @Test public void manyToManyTest() { UnitMapper unitMapper = session.getMapper(UnitMapper.class); User userOrder = unitMapper.manyToManyTest(1); System.out.println(userOrder); // 關閉會話 session.close(); }
標簽即屬性說明
Association標簽: 作用是可以將關聯查詢信息映射到一個pojo對象中
collection標簽: 作用是可以將關聯查詢信息映射到一個集合中
Association和collection標簽常用到的屬性:
Property屬性: 指定當前association標簽內容映射到pojo對象中哪個屬性。
javaType:映射屬性的類型
typeHandler:類型處理器,使用這個屬性,你可以覆蓋默認的 typeHandler 類型處理器。 這個屬性值是類的完全限定名或者是一個類型處理器的實現, 或者是類型別名
column:sql結果集列名,用在嵌套查詢時傳遞參數,要 處 理 復 合 主 鍵 , 你 可 以 指 定 多 個 列 名 通 過 column= ” {prop1=col1,prop2=col2} ” 這種語法來傳遞給嵌套查詢語 句。prop1 和 prop2 以參數對象形式來設置給目標嵌套查詢語句。
select:嵌套查詢映射語句的ID
fetchType:可選的。有效值為 lazy和eager。 如果使用了,它將取代全局配置參數lazyLoadingEnabled
columnPrefix:將含有指定前綴的結果集映射到當前標簽下 例如:<association property="productInfo" columnPrefix="p_" /> 將結果集中以“p_”開頭的列映射到productInfo屬性上