@(MyBatis)[Java, 框架, MyBatis]前端
案例:查詢全部訂單信息,關聯查詢下單用戶信息java
從Order的角度,一個訂單對應一個用戶:order----->user (一對一)
從User的角度,一個用戶能夠有多個訂單:user------>order (多對一)sql
場景需求:查詢訂單,同時關聯查詢用戶信息
-------------------------------表設計------------------------------------------
在t_orders(多方表)中增長一個字段user_id,做爲外鍵數據庫
-------------------------------類設計------------------------------------------session
public class Order { private int oId; private int number; private User user; //省略get、set方法 } public class User { private Integer userId; private String userName; //省略get、set方法 }
-----------------------映射文件mapping.xml-----------------------mybatis
<association/>
關聯元素處理「有一個」類型的關係。例如,在下面的<resultMap/>
中表示一個訂單對應了一個用戶。
association標籤可用的屬性以下:app
property:對象屬性的名稱 javaType:對象屬性的類型 column:所對應的外鍵字段名稱 select:使用另外一個查詢封裝的結果 columnPrefix:當鏈接多表時,你將不得不使用列別名來避免ResultSet中的重複列名。指定columnPrefix容許你映射列名到一個外部的結果集中。
<select id="getOrder" parameterType="int" resultMap="orderResultMap "> select t_orders.order_id,t_orders.order_number,t_user.id,t_user.user_name,t_user.user_address from t_orders,t_user <where> t_orders.user_id = t_user.id and t_orders.order_id = #{oId} </where> </select> <resultMap type="com.george.pojo.Order" id="orderResultMap "> <id property="oId" column="order_id"/> <result property="number" column="order_number"/> <association property="user" resultMap="userResultMap "/> </resultMap> <resultMap type="com.george.pojo.User" id="userResultMap "> <id property="userId" column="id"/> <result property="userName" column="user_name"/> </resultMap>
執行過程:框架
DEBUG [main] - ==> Preparing: select t_orders.order_id, t_orders.order_number, t_user.id, t_user.user_name, t_user.user_address from t_orders,t_user WHERE t_orders.user_id = t_user.id and t_orders.order_id = ? DEBUG [main] - ==> Parameters: 1(Integer) TRACE [main] - <== Columns: order_id, order_number, id, user_name, user_address TRACE [main] - <== Row: 1, 30, 1, zhangsan, 北京 DEBUG [main] - <== Total: 1 打印查詢結果對象: Order [oId=1, number=30, user=User [userId=1, userName=zhangsan]]
經過執行另一個SQL映射語句來返回預期的複雜類型ide
<select id="getOrder" parameterType="int" resultMap="orderResultMap"> select order_id, order_number, user_id from t_orders <where> order_id = #{oIddddd} </where> </select> <resultMap type="com.george.bean.Order" id="orderResultMap"> <id property="oId" column="order_id"/> <result property="number" column="order_number"/> <association property="user" javaType="com.george.bean.User" column="user_id" select="getUser"/> </resultMap> <select id="getUser" parameterType="int" resultType="com.george.bean.User"> <!-- 使用別名來映射字段和屬性 --> select user_id as userId , user_name as userName from t_user <where> user_id=#{idddd} <!-- 外鍵字段或者和外鍵關聯那個字段的值,由上一條查詢中獲得 --> </where> </select>
---------------------------java代碼---------------------性能
@Test public void test() { SqlSession session = DBUtil.getSession(); Order order = session.selectOne("getOrder",1 ); System.out.println("打印查詢結果對象:"+order); }
--------------------------sql執行過程-------------------------
DEBUG [main] - ==> Preparing: select order_id, order_number, user_id from t_orders WHERE order_id = ? DEBUG [main] - ==> Parameters: 1(Integer) TRACE [main] - <== Columns: order_id, order_number, user_id TRACE [main] - <== Row: 1, 30, 3 DEBUG [main] - ====> Preparing: select user_id as userId , user_name as userName from t_user WHERE user_id=? DEBUG [main] - ====> Parameters: 3(Integer) TRACE [main] - <==== Columns: userId, userName TRACE [main] - <==== Row: 3, wangwu DEBUG [main] - <==== Total: 1 DEBUG [main] - <== Total: 1 打印查詢結果對象:Order [oId=1, number=30, user=User [userId=3, userName=wangwu]]
__注意__:咱們有兩個查詢語句:一個來加載order,另一個來加載user。
這種方式很簡單, 但會產生 「N+1 查詢問題」。歸納地講,N+1 查詢問題能夠是這樣引發的:
(1)你執行了一個單獨的 SQL 語句來獲取結果列表(就是「+1」)。 (2)假設返回的是一個集合,集合中的每一個元素對應了一條記錄,你還須要執行了一個查詢語句來爲每條記錄加載細節(就是「N」)。
MyBatis 延遲加載是一種處理方式,然而,若是你加載一個列表,以後迅速迭代來訪問嵌套的數據,你會調用全部的延遲加載,問題沒有解決。
public class OrdersCustom extends Orders { private String username ; // 用戶名稱 private String address ; // 用戶地址 省略get、set方法 }
<!-- OrdersCustom 類繼承 Orders 類後 OrdersCustom 類包括了 Orders 類的全部字段, 只須要定義用戶的信息字段便可。 --> <select id ="findOrdersList" resultType ="cn.george.mybatis.po.OrdersCustom" parameterType="Integer"> SELECT orders.*, user.user_name, user.user_address FROM orders, user WHERE orders.user_id = #{user.id} </select>
總結:定義專門的po類做爲輸出類型,其中定義了sql查詢結果集全部的字段。此方法較爲簡單,企業中廣泛使用。
案例:經過訂單id查詢訂單信息及訂單下的訂單明細信息。
訂單信息與訂單明細爲一對多關係。
---------------------------類設計----------------------------------------
private int oId; private int number; //訂單編號 private User user; //用戶信息 private List<Orderdetail> orderdetails; //訂單詳情信息 //省略get、set方法 }
-------------------------映射文件-----------------------------
<select id="getOrder" parameterType="int" resultMap="orderdetailResultMap"> select t_orders.order_id, t_orders.order_number, t_user.user_id, t_user.user_name, t_user.user_address, t_orderdetail.od_id, t_orderdetail.items_id, t_orderdetail.items_num, t_orderdetail.order_id from t_orders,t_user,t_orderdetail <where> t_orderdetail.order_id = t_orders.order_id and t_orders.user_id = t_user.user_id and t_orders.order_id = #{id} </where> </select> <resultMap type="com.hh.bean.Order" id="orderdetailResultMap"> <id property="oId" column="order_id" /> <result property="number" column="order_number" /> <association property="user" javaType="com.hh.bean.User"> <id property="userId" column="user_id" /> <result property="userName" column="user_name" /> </association> <!-- 集合的泛型,指定集合中的對象類型--> <collection property="orderdetails" ofType="com.hh.bean.Orderdetail"> <id property="odId" column="od_id" /> <result property="itemsId" column="items_id" /> <result property="itemsNum" column="items_num" /> </collection> </resultMap>
使用繼承extends,上面的<resultMap/>能夠改寫爲:
<!--關聯查詢用戶--> <resultMap type="com.hh.bean.Order" id="orderResultMap"> <id property="oId" column="order_id" /> <result property="number" column="order_number" /> <association property="user" javaType="com.hh.bean.User"> <id property="userId" column="user_id" /> <result property="userName" column="user_name" /> </association> </resultMap>
<!--繼承上一個resultMap,關聯查詢用戶和訂單詳情--> <resultMap type="com.hh.bean.Order" id="orderdetailResultMap" extends="orderResultMap"> <!-- 集合的泛型,指定集合中的對象類型 --> <collection property="orderdetails" ofType="com.hh.bean.Orderdetail"> <id property="odId" column="od_id" /> <result property="itemsId" column="items_id" /> <result property="itemsNum" column="items_num" /> </collection> </resultMap>
打印查詢結果對象:Order [oId=1, number=30000001, user=User [userId=3, userName=wangwu], orderdetails=[Orderdetail [odId=1, itemsId=2, itemsNum=30], Orderdetail [odId=2, itemsId=1, itemsNum=32]]]
<select id="getOrder" parameterType="int" resultMap="orderResultMap"> select order_id, order_number, user_id from t_orders <where> order_id = #{oIddddd} </where> </select> <resultMap type="com.hh.bean.Order" id="orderResultMap"> <id property="oId" column="order_id" /> <result property="number" column="order_number" /> <association property="user" javaType="com.hh.bean.User" column="user_id" select="getUser" /> <!-- column 哪個字段和多方表中的外鍵對應 --> <collection property="orderdetails" column="order_id" select="getOrderdeatil"/> </resultMap> <select id="getUser" parameterType="int" resultType="com.hh.bean.User"> <!-- 使用別名來映射字段和屬性 --> select user_id as userId , user_name as userName from t_user <where> user_id=#{idddd} <!-- 外鍵字段或者和外鍵關聯那個字段的值,由上一條查詢中獲得 --> </where> </select> <select id="getOrderdeatil" parameterType="int" resultType="com.hh.bean.Orderdetail"> select od_id as odId,items_id as itemsId,items_num as itemsNum from t_orderdetail <where> order_id = #{id} <!-- 外鍵字段或者和外鍵關聯那個字段的值,由上一條查詢中獲得 --> </where> </select>
---------------------------java代碼---------------------
@Test public void test() { SqlSession session = DBUtil.getSession(); Order order = session.selectOne("getOrder",1 ); System.out.println("打印查詢結果對象:"+order); }
------------------------sql執行結果-----------------------
打印查詢結果對象:Order [oId=1, number=30000001, user=User [userId=3, userName=wangwu], orderdetails=[Orderdetail [odId=1, itemsId=2, itemsNum=30], Orderdetail [odId=2, itemsId=1, itemsNum=32]]]
案例:查詢用戶購買的商品信息
須要查詢全部用戶信息,關聯查詢訂單及訂單明細信息,訂單明細信息中關聯查詢商品信息
分析:
須要關聯查詢映射的信息是:訂單、訂單明細、商品信息
---------------------------類設計-----------------------------------
public class User { private Integer userId; private String userName; private List<Order> orders; //訂單列表 } public class Order { private int oId; private int number; //訂單編號 private List<Orderdetail> orderdetails; //訂單詳情信息 } public class Orderdetail { private int odId; private Items item; //商品信息 private int itemsNum; //商品數量 } public class Items { private int itemId; private String itemName; //商品名稱 }
-----------------------------映射文件--------------------------------------
<select id="getUser" resultMap="userOrderListResultMap"> select t_orders.order_id, t_orders.order_number, t_user.user_id, t_user.user_name, t_user.user_address, t_orderdetail.od_id, t_orderdetail.items_id, t_orderdetail.items_num, t_orderdetail.order_id, t_items.item_id, t_items.item_name from t_orders,t_user,t_orderdetail,t_items <where> t_orderdetail.order_id = t_orders.order_id and t_orders.user_id = t_user.user_id and t_orderdetail.items_id = t_items.item_id </where> </select> <resultMap type="com.hh.bean.User" id="userOrderListResultMap"> <id property="userId" column="user_id" /> <result property="userName" column="user_name" /> <collection property="orders" ofType="com.hh.bean.Order"> <id property="oId" column="order_id" /> <result property="number" column="order_number" /> <collection property="orderdetails" ofType="com.hh.bean.Orderdetail"> <id property="odId" column="od_id" /> <result property="itemsNum" column="items_num" /> <association property="item" javaType="com.hh.bean.Items"> <id property="itemId" column="item_id" /> <result property="itemName" column="item_name" /> </association> </collection> </collection> </resultMap>
---------------------------java代碼---------------------
@Test public void test() { SqlSession session = DBUtil.getSession(); List<User> users = session.selectList("getUser"); for (User user : users) { System.out.println("打印用戶信息:"+user); } }
------------------------sql執行結果-----------------------
打印用戶信息:User [userId=1, userName=zhangsan, orders=[Order [oId=1, number=30000001, orderdetails=[Orderdetail [odId=1, item=Items [itemId=2, itemName=香蕉], itemsNum=30], Orderdetail [odId=2, item=Items [itemId=1, itemName=蘋果], itemsNum=32]]], Order [oId=3, number=30000003, orderdetails=[Orderdetail [odId=3, item=Items [itemId=3, itemName=橘子], itemsNum=25]]]]] 打印用戶信息:User [userId=3, userName=wangwu, orders=[Order [oId=4, number=30000004, orderdetails=[Orderdetail [odId=4, item=Items [itemId=3, itemName=橘子], itemsNum=45]]]]]
什麼是延遲加載
resultMap可實現高級映射(使用association、collection實現一對一及一對多映射),association、collection具有延遲加載功能。
例如:先從單表查詢,須要時再從關聯表去關聯查詢,大大 提升數據庫性能 ,由於查詢單表要比關聯查詢多張錶速度要快。
mybatis框架默認不支持延遲加載功能,若是想要使用,須要啓用延遲加載功能
<!-- 打開延遲加載的開關 --> <setting name="lazyLoadingEnabled" value="true" />
默認框架會採用侵入式的延遲加載:
能夠禁用侵入式延遲加載功能
<!-- 將積極加載改成消息加載即按需加載 --> <setting name="aggressiveLazyLoading" value="false" />
綜上所述,在主配置文件mybatis-config.xml中作以下配置:
<settings> <setting name="lazyLoadingEnabled" value ="true" /> <setting name="aggressiveLazyLoading" value ="false" /> </settings>
注意一個異常:Caused by: org.xml.sax.SAXParseException; lineNumber: 36; columnNumber: 17; 元素類型爲 "configuration" 的內容必須匹配 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"。
在關聯的元素(association ,collection ,discriminator)中,咱們存在一個屬性: fetchType來決定是否須要延遲加載,若是配置它,它將覆蓋掉原有在MyBatis設置的策略。
對於它而言,它有兩個取值:
* lazy: 延遲加載(默認) * eager:即刻加載
由它來決定是否須要延遲或者即刻加載。
做用:
場合:
做用:
場合:
做用:
場合:
若是使用 resultType 沒法將查詢結果映射到 list 集合中。