Mybatis關係對映
一、一對一關係對映
使用resultType+包裝類實現
1、假設問題背景是要求在某一個購物平臺的後臺程式中新增一個這樣的功能:查詢某個訂單的資訊和下該訂單的使用者資訊。首先我們可以知道,一般這樣的平臺上面,某一筆訂單隻屬於某一個使用者,從這個角度來看,可以作為一對一的參考模型
①首先建立資料表user(使用者表)
CREATE TABLE `user` ( `uid` INT(11) NOT NULL AUTO_INCREMENT, `username` VARCHAR(255) DEFAULT NULL, `password` VARCHAR(255) DEFAULT NULL, `sex` VARCHAR(255) DEFAULT NULL, PRIMARY KEY (`uid`) ) ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
建立orders表(所用的訂單表)
CREATE TABLE `orders` ( `oid` INT(11) NOT NULL AUTO_INCREMENT, `total` DOUBLE DEFAULT NULL, `ordertime` DATETIME DEFAULT NULL, `name` VARCHAR(20) DEFAULT NULL, `uid` INT(11) DEFAULT NULL, PRIMARY KEY (`oid`), KEY `FKC3DF62E5AA3D9C7` (`uid`), CONSTRAINT `FKC3DF62E5AA3D9C7` FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ) ENGINE=INNODB AUTO_INCREMENT=9004 DEFAULT CHARSET=utf8
如下圖所示:
②建立User和Order的實體類
1 package cn.mybatis.po; 2 3 public class User { 4private int uid; 5private String username; 6private String password; 7private String address; 8private String sex; 9 10public int getUid() { 11return uid; 12} 13 14public void setUid(int uid) { 15this.uid = uid; 16} 17 18public String getUsername() { 19return username; 20} 21 22public String getPassword() { 23return password; 24} 25 26public String getAddress() { 27return address; 28} 29 30public String getSex() { 31return sex; 32} 33 34public void setUsername(String username) { 35this.username = username; 36} 37 38public void setPassword(String password) { 39this.password = password; 40} 41 42public void setAddress(String address) { 43this.address = address; 44} 45 46public void setSex(String sex) { 47this.sex = sex; 48} 49 50public User(String username, String password, String address, String sex) { 51this.username = username; 52this.password = password; 53this.address = address; 54this.sex = sex; 55} 56 57public User() { 58} 59 60@Override 61public String toString() { 62return "User{" + 63"uid=" + uid + 64", username='" + username + '\'' + 65", password='" + password + '\'' + 66", address='" + address + '\'' + 67", sex='" + sex + '\'' + 68'}'; 69} 70 } User類
1 package cn.mybatis.po; 2 3 import java.util.Date; 4 5 public class Order { 6 7private int oid; 8private double total; 9private Date ordettime; 10private String name; 11 12public int getOid() { 13return oid; 14} 15 16public void setOid(int oid) { 17this.oid = oid; 18} 19 20public double getTotal() { 21return total; 22} 23 24public void setTotal(double total) { 25this.total = total; 26} 27 28public Date getOrdettime() { 29return ordettime; 30} 31 32public void setOrdettime(Date ordettime) { 33this.ordettime = ordettime; 34} 35 36public String getName() { 37return name; 38} 39 40public void setName(String name) { 41this.name = name; 42} 43 44@Override 45public String toString() { 46return "Order{" + 47"oid=" + oid + 48", total=" + total + 49", ordettime=" + ordettime + 50", name='" + name + '\'' + 51'}'; 52} 53 } Order類
③用於需要同時查詢User和Order的資訊,所以需要用到上一篇中講到的POJO輸出對映型別。具體來說就是,需要自定義一個OrderPoJo,其中包含我們要查詢的Order和User資訊,定義的OrderPoJo型別如下(這裡我們可以使用繼承的方式,如果我們查詢結果中哪一個類的要查詢結果多就繼承該類,可以簡便包裝類的編寫)
1 package cn.mybatis.po; 2 3 public class OrderPoJo extends Order{ 4 5private String username; 6private String address; 7private String sex; 8 9public String getUsername() { 10return username; 11} 12 13public void setUsername(String username) { 14this.username = username; 15} 16 17public String getAddress() { 18return address; 19} 20 21public void setAddress(String address) { 22this.address = address; 23} 24 25public String getSex() { 26return sex; 27} 28 29public void setSex(String sex) { 30this.sex = sex; 31} 32 33@Override 34public String toString() { 35return "OrderPoJo{" + 36super.toString() + 37"username='" + username + '\'' + 38", address='" + address + '\'' + 39", sex='" + sex + '\'' + 40'}'; 41} 42 }
④編寫好對應的實體類和包裝類之後就開始寫,mapper配置檔案和mapper.java。mapper配置檔案中我們只需要根據問題背景寫好相應的Sql語句就好
這是編寫好Sql的Mapper配置檔案
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <!--mapper為根元素,namespace指定了名稱空間--> 6 <mapper namespace="cn.mybatis.mapper.OrderMapper"> 7 8<select id="findOrderAndUser" parameterType="int" resultType="orderPoJo"> 9SELECT orders.*, 10user.username,user.sex,user.address 11FROM orders,USER 12WHERE oid = #{id} AND user.uid = orders.uid 13</select> 14 15 </mapper>
下面是mapper介面中的一個方法,由於我們只需要完成這一個問題,所以OrderMapper介面也比較簡單
⑤編寫好所有的檔案後,使用Junit來測試檔案
1 package cn.mybatis.mapper; 2 3 4 import cn.mybatis.po.OrderPoJo; 5 import org.apache.ibatis.io.Resources; 6 import org.apache.ibatis.session.SqlSession; 7 import org.apache.ibatis.session.SqlSessionFactory; 8 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 9 import org.junit.Before; 10 import org.junit.Test; 11 12 import java.io.InputStream; 13 14 public class OrderMapperTest { 15 16private SqlSessionFactory sqlSessionFactory; 17 18@Before 19public void setUp() throws Exception { 20InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 21sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 22} 23 24@Test 25public void testFindOrderAndUser() throws Exception { 26SqlSession sqlSession = sqlSessionFactory.openSession(); 27OrderMapper orderMapper = sqlSession.getMapper(OrderMapper.class); 28 29OrderPoJo orderPoJo = orderMapper.findOrderAndUser(3); 30 31System.out.println(orderPoJo); 32 33sqlSession.close(); 34 35} 36 37 }
⑥我們來通過日誌分析一下結果:最終能夠按照既定的Sql查詢出響應的結果
使用resultMap在配置檔案中實現
1、我們使用resultMap來進行測試的時候,首先需要注意的是,由於沒有自定義包裝型別,所以需要在原始的Order中新增User型別的屬性,保證可以在Mapper配置檔案中將查詢到的User屬性配置到user中,從而得到關聯查詢結果
1 package cn.mybatis.po; 2 3 import java.util.Date; 4 5 public class Order { 6 7private int oid; 8private double total; 9private Date ordertime; 10private String name; 11private User user; 12 13public int getOid() { 14return oid; 15} 16 17public void setOid(int oid) { 18this.oid = oid; 19} 20 21public double getTotal() { 22return total; 23} 24 25public void setTotal(double total) { 26this.total = total; 27} 28 29public Date getOrdertime() { 30return ordertime; 31} 32 33public void setOrdertime(Date ordertime) { 34this.ordertime = ordertime; 35} 36 37public String getName() { 38return name; 39} 40 41public User getUser() { 42return user; 43} 44 45public void setUser(User user) { 46this.user = user; 47} 48 49public void setName(String name) { 50this.name = name; 51} 52 53@Override 54public String toString() { 55return "Order{" + 56"oid=" + oid + 57", total=" + total + 58", ordertime=" + ordertime + 59", name='" + name + '\'' + 60", user=" + user + 61'}'; 62} 63 }
2、相關類和上面的內容一樣,我們使用resultMap來實現,顯然需要在Mapper中配置resultMap
1<!-- 2訂單關聯User的查詢resultMap 3將查詢的結果全部對映到Order類中 4--> 5<resultMap id="OrderResultMap" type="cn.mybatis.po.Order"> 6<!--配置對映訂單--> 7<id column="oid" property="oid"></id> 8<result column="total" property="total"></result> 9<result column="ordertime" property="ordertime"></result> 10<result column="name" property="name"></result> 11 12<!--配置關聯使用者資訊--> 13<!-- 14association:用於對映關聯查詢單個物件的資訊 15property:用於設定將關聯資訊對映到Order的哪個屬性中 16--> 17<association property="user" javaType="cn.mybatis.po.User"> 18<id column="uid" property="uid"></id> 19<result column="username" property="username"></result> 20<result column="address" property="address"></result> 21<result column="sex" property="sex"></result> 22</association> 23</resultMap>
3、然後在Mapper配置檔案中使用resultMap型別的statment
<select id="findOrderAndUserByResultMap" parameterType="int" resultMap="OrderResultMap"> SELECT orders.*, user.username,user.sex,user.address FROM orders,USER WHERE oid = #{id} AND user.uid = orders.uid </select>
4、結果同使用resultType的結果一樣
二、一對多關係對映
1、我們先分析一下orderitem和orders兩張資料表的關係,我們能夠想到一條訂單中包含許多詳細的訂單條目資訊,所以簡單的得到下面的關係
2、在上面的基礎上,我們再建立一張orderitem資料表
CREATE TABLE `orderitem` ( `itemid` INT(11) NOT NULL AUTO_INCREMENT, `count` INT(11) DEFAULT NULL, `subtotal` DOUBLE DEFAULT NULL, `pid` INT(11) DEFAULT NULL, `oid` INT(11) DEFAULT NULL, PRIMARY KEY (`itemid`), KEY(`oid`), KEY(`pid`), KEY(`oid`), FOREIGN KEY (`oid`) REFERENCES `orders` (`oid`), FOREIGN KEY (`pid`) REFERENCES `product` (`pid`) ) ENGINE=INNODB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8
3、我們再建立相應的實體類orderitem
1 package cn.mybatis.po; 2 3 public class OrderItem { 4 5private int itemid; 6private int count; 7private double subtotal; 8private int pid; 9 10public int getItemid() { 11return itemid; 12} 13 14public void setItemid(int itemid) { 15this.itemid = itemid; 16} 17 18public int getCount() { 19return count; 20} 21 22public void setCount(int count) { 23this.count = count; 24} 25 26public double getSubtotal() { 27return subtotal; 28} 29 30public void setSubtotal(double subtotal) { 31this.subtotal = subtotal; 32} 33 34public int getPid() { 35return pid; 36} 37 38public void setPid(int pid) { 39this.pid = pid; 40} 41 42@Override 43public String toString() { 44return "OrderItem{" + 45"itemid=" + itemid + 46", count=" + count + 47", subtotal=" + subtotal + 48", pid=" + pid + 49'}'; 50} 51 } orderitem實體類
4、類似於上面講到的使用resultMap的方式,我們在Order中新增上orderitem這一屬性,作用就是維護Order和Orderitem的關聯關係
1 package cn.mybatis.po; 2 3 import java.util.Date; 4 import java.util.List; 5 6 public class Order { 7 8private int oid; 9private double total; 10private Date ordertime; 11private String name; 12private User user; 13 14private List<OrderItem> orderItems; 15 16public int getOid() { 17return oid; 18} 19 20public void setOid(int oid) { 21this.oid = oid; 22} 23 24public double getTotal() { 25return total; 26} 27 28public void setTotal(double total) { 29this.total = total; 30} 31 32public Date getOrdertime() { 33return ordertime; 34} 35 36public void setOrdertime(Date ordertime) { 37this.ordertime = ordertime; 38} 39 40public String getName() { 41return name; 42} 43 44public User getUser() { 45return user; 46} 47 48public void setUser(User user) { 49this.user = user; 50} 51 52public void setName(String name) { 53this.name = name; 54} 55 56public List<OrderItem> getOrderItems() { 57return orderItems; 58} 59 60public void setOrderItems(List<OrderItem> orderItems) { 61this.orderItems = orderItems; 62} 63 64@Override 65public String toString() { 66return "Order{" + 67"oid=" + oid + 68", total=" + total + 69", ordertime=" + ordertime + 70", name='" + name + '\'' + 71", user=" + user + 72", orderItems=" + orderItems + 73'}'; 74} 75 } 修改後的Order類
5、我們先配置Mapper檔案。使用collection配置實體類中的List屬性(List<OrderItem>)
<!--查詢Order和OrderItem的配置(一對多關係查詢配置)--> <resultMap id="OrderAndOrderItemByResultMap" type="cn.mybatis.po.Order" extends="OrderResultMap"> <!--使用extends,可以將某一段resultMap繼承過來--> <!--OrderItem資訊--> <!--collections:將查詢到的多條資訊對映到集合中 property:將查詢到的多條記錄對映到Order中的相應屬性中(orderItems) ofType:指的是要對映的集合中的JavaType--> <collection property="orderItems" ofType="cn.mybatis.po.OrderItem"> <id column="itemid" property="itemid"></id> <result column="count" property="count"></result> <result column="subtotal" property="subtotal"></result> </collection> </resultMap>
6、然後將上面配置的resultMap加入到statment中
<select id="findOrderAndOrderItemByResultMap" parameterType="int" resultMap="OrderAndOrderItemByResultMap"> SELECT orders.*, user.username, user.sex, user.address, orderitem.itemid, orderitem.count, orderitem.subtotal FROM orders, USER, orderitem WHERE orders.oid = #{id} AND user.uid = orders.uid AND orderitem.oid = orders.oid </select>
7、然後在Mapper介面中新增測試方法
//一對多關係測試 public Order findOrderAndOrderItemByResultMap(int id) throws Exception;
8、將查詢的結果進行輸出可以發現能夠正常查詢出想要的結果
Order{oid=2, total=32.0, ordertime=Thu Dec 26 21:47:04 CST 2019, name='Lucy', user=User{uid=2, username='Rose', password='null', address='武漢市', sex='women'}, orderItems=[OrderItem{itemid=2, count=21, subtotal=32.0, pid=0}, OrderItem{itemid=4, count=32, subtotal=54.0, pid=0}]}
三、多對多關係對映
1、問題背景就是查詢User所購買的商品詳細資訊,即查詢結果包括User資訊和Product資訊。我們先分析一下整個資料表之間的關係如下圖所示
2、在上面分析的基礎上,我們建立Product資料表的對應的Product實體類
CREATE TABLE `product` ( `pid` INT(11) NOT NULL AUTO_INCREMENT, `pname` VARCHAR(255) DEFAULT NULL, `shop_price` DOUBLE DEFAULT NULL, PRIMARY KEY (`pid`) ) ENGINE=INNODB AUTO_INCREMENT=80 DEFAULT CHARSET=utf8
1 package cn.mybatis.po; 2 3 public class Product { 4private int pid; 5private String pname; 6private double shop_price; 7 8public int getPid() { 9return pid; 10} 11 12public void setPid(int pid) { 13this.pid = pid; 14} 15 16public String getPname() { 17return pname; 18} 19 20public void setPname(String pname) { 21this.pname = pname; 22} 23 24public double getShop_price() { 25return shop_price; 26} 27 28public void setShop_price(double shop_price) { 29this.shop_price = shop_price; 30} 31 32@Override 33public String toString() { 34return "Product{" + 35"pid=" + pid + 36", pname='" + pname + '\'' + 37", shop_price=" + shop_price + 38'}'; 39} 40 } Product實體類
3、一般編寫Mapper配置檔案可以使用resultMap或者resultType(+自定義擴充套件型別)來實現,這裡,我們使用resultMap在Mapper配置檔案中進行,所以需要在OrderItem類中新增Product型別的屬性,修改後的OrderItem類如下
1 package cn.mybatis.po; 2 3 public class OrderItem { 4 5private int itemid; 6private int count; 7private double subtotal; 8 9private Product product; 10 11public int getItemid() { 12return itemid; 13} 14 15public void setItemid(int itemid) { 16this.itemid = itemid; 17} 18 19public int getCount() { 20return count; 21} 22 23public void setCount(int count) { 24this.count = count; 25} 26 27public double getSubtotal() { 28return subtotal; 29} 30 31public void setSubtotal(double subtotal) { 32this.subtotal = subtotal; 33} 34 35public Product getProduct() { 36return product; 37} 38 39public void setProduct(Product product) { 40this.product = product; 41} 42 43@Override 44public String toString() { 45return "OrderItem{" + 46"itemid=" + itemid + 47", count=" + count + 48", subtotal=" + subtotal + 49", product=" + product + 50'}'; 51} 52 } 修改的OrderItem類
4、修改相應的實體類後,可以再Mapper配置檔案中配置查詢結果User資訊以及關聯的Order、OrderItem和所要的Product資訊,配置如下
<!--查詢User和Product關係結果(多對多關係)--> <resultMap id="UserAndProductResultMap" type="cn.mybatis.po.User"> <!--User資訊--> <id column="uid" property="uid"></id> <result column="username" property="username"></result> <result column="address" property="address"></result> <result column="sex" property="sex"></result> <!--Order資訊--> <collection property="orders" ofType="cn.mybatis.po.Order"> <id column="oid" property="oid"></id> <result column="total" property="total"></result> <result column="ordertime" property="ordertime"></result> <result column="name" property="name"></result> <!--OrderItem資訊--> <collection property="orderItems" ofType="cn.mybatis.po.OrderItem"> <id column="itemid" property="itemid"></id> <result column="count" property="count"></result> <result column="subtotal" property="subtotal"></result> <association property="product" javaType="cn.mybatis.po.Product"> <id column="pid" property="pid"></id> <result column="pname" property="pname"></result> <result column="shop_price" property="shop_price"></result> </association> </collection> </collection> </resultMap>
5、然後編寫響應的Sql,並新增到Mapper中的statment中
<select id="findUsersAndProduct" parameterType="int" resultMap="UserAndProductResultMap"> SELECT orders.*, user.username, user.sex, user.address, orderitem.itemid, orderitem.count, orderitem.subtotal, product.pid, product.pname, product.shop_price FROM orders, USER, orderitem, product WHERE user.uid = orders.uid AND orderitem.oid = orders.oid </select>
6、在Mapper.java中新增響應的方法
public List<User> findUsersAndProduct() throws Exception;
7、最後使用Junit測試結果如下