MyBatis理解與掌握(關聯查詢)

MyBatis理解與掌握(關聯查詢)

@(MyBatis)[Java, 框架, MyBatis]前端

一對一查詢

案例:查詢全部訂單信息,關聯查詢下單用戶信息java

clipboard.png

從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 延遲加載是一種處理方式,然而,若是你加載一個列表,以後迅速迭代來訪問嵌套的數據,你會調用全部的延遲加載,問題沒有解決。

方法三:增長一個包含訂單信息和用戶信息的類,用這個類做爲返回類型 resultType

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查詢訂單信息及訂單下的訂單明細信息。
訂單信息與訂單明細爲一對多關係。

clipboard.png

---------------------------類設計----------------------------------------

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]]]



多對多查詢

案例:查詢用戶購買的商品信息
須要查詢全部用戶信息,關聯查詢訂單及訂單明細信息,訂單明細信息中關聯查詢商品信息

分析:
須要關聯查詢映射的信息是:訂單、訂單明細、商品信息

  • 訂單:一個用戶對應多個訂單,使用 collection 映射到用戶對象的訂單列表屬性中
  • 訂單明細:一個訂單對應多個明細,使用 collection 映射到訂單對象中的明細屬性中
  • 商品信息: 一個訂單明細對應一個商品, 使用 association 映射到訂單明細對象的商品屬性中。

clipboard.png

---------------------------類設計-----------------------------------

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]]]]]

自關聯

clipboard.png

clipboard.png

延遲加載

什麼是延遲加載

resultMap可實現高級映射(使用association、collection實現一對一及一對多映射),association、collection具有延遲加載功能。

例如:先從單表查詢,須要時再從關聯表去關聯查詢,大大 提升數據庫性能 ,由於查詢單表要比關聯查詢多張錶速度要快。

clipboard.png

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?)"。
Alt text
在關聯的元素(association ,collection ,discriminator)中,咱們存在一個屬性: fetchType來決定是否須要延遲加載,若是配置它,它將覆蓋掉原有在MyBatis設置的策略。

對於它而言,它有兩個取值:

* lazy: 延遲加載(默認)
* eager:即刻加載

由它來決定是否須要延遲或者即刻加載。

總結

resultType

做用:

  • 將查詢結果按照 sql 列名 pojo 屬性名一致性映射到 pojo 中。

場合:

  • 常見一些明細記錄的展現, 好比用戶購買商品明細, 將關聯查詢信息所有展現在頁面時,此時可直接使用 resultType 將每一條記錄映射到 pojo 中, 在前端頁面遍歷 list ( list 中是 pojo )便可。

resultMap

  • 使用 association 和 collection 完成一對一和一對多高級映射 (對結果有特殊的映射要求) 。

association

做用:

  • 將關聯查詢信息映射到一個 pojo 對象中。

場合:

  • 爲了方便查詢關聯信息可使用 association 將關聯訂單信息映射爲用戶對象的 pojo 屬性中,好比:查詢訂單及關聯用戶信息。使用 resultType 沒法將查詢結果映射到 pojo 對象的 pojo 屬性中,根據對結果集查詢遍歷的須要選擇使用 resultType 仍是 resultMap 。

collection

做用:

  • 將關聯查詢信息映射到一個 list 集合中。

場合:

  • 爲了方便查詢遍歷關聯信息可使用 collection 將關聯信息映射到 list 集合中,好比:查詢用戶權限範圍模塊及模塊下的菜單,可以使用 collection 將模塊映射到模塊 list 中,將菜單列表映射到模塊對象的菜單 list 屬性中, 這樣的做的目的也是方便對查詢結果集進行遍歷查詢。

若是使用 resultType 沒法將查詢結果映射到 list 集合中。

相關文章
相關標籤/搜索