首頁 > 軟體

MyBatis延遲載入策略深入探究

2022-07-30 14:01:10

有兩種寫法來表示查詢資訊,分別是連結串列查詢和分步查詢的方法。那麼既然我麼能用一個SQL語句能夠執行完,那為什麼還要分開來寫呢?

原因很簡單:可以發現如果我們把他們連在一起那麼他們就是一個多表查詢語句,如果不放在一起執行,那那就是單獨一個表的查詢語句。但是這需要我們設定mybatis的延遲載入(懶載入)

分步查詢的優點

**可以實現延遲載入,**但是必須在核心組態檔中設定全域性設定資訊

lazyLoadingEnabled:延遲載入的全域性開關。當開啟時,所有關聯物件都會延遲載入

aggressiveLazyLoding:當開啟時,任何方式的呼叫都會載入該物件的所有屬性。否則,該屬性會按需載入 ,此時就可以實現按需載入,需要獲取的資料是什麼,就只會執行相應的sql.此時會通過association和collection中的fetchType屬性設定當前的分步查詢是否使用懶載入

fetchType=“lazy(延遲載入) | eager(立即載入)”

在主組態檔當中設定延遲載入

延遲載入:在SqlMapConfig.xml中設定延遲載入檔案

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING" />  <!-- 列印紀錄檔到控制檯上 -->
    <setting name="lazyLoadingEnabled" value="true"/>   
    <setting name="aggressiveLazyLoading" value="false"/>   <!-- false才會按需載入,不開啟呼叫所有的物件 -->
</settings>

在studentDao.xml當中設定分步查詢

測試結果

此時開啟了懶載入,實現了分佈查詢

如何使用

public class UserDO {
  private Integer userId;
  private String username;
  private String password;
  private String nickname;
  private List<PermitDO> permitDOList;
  public UserDO() {}
}
<resultMap id="BaseMap" type="org.apache.ibatis.study.entity.UserDO">
    <id column="user_id" jdbcType="INTEGER" property="userId" />
    <result column="username" jdbcType="VARCHAR" property="username" />
    <result column="password" jdbcType="VARCHAR" property="password" />
    <result column="nickname" jdbcType="VARCHAR" property="nickname"/>
    <collection property="permitDOList" column="user_id" select="getPermitsByUserId"
     fetchType="lazy">
    </collection>
</resultMap>
  <resultMap id="PermitBaseMap" type="org.apache.ibatis.study.entity.PermitDO">
    <id column="id" jdbcType="INTEGER" property="id"/>
    <result column="code" jdbcType="VARCHAR" property="code"/>
    <result column="name" jdbcType="VARCHAR" property="name"/>
    <result column="type" jdbcType="TINYINT" property="type"/>
    <result column="pid" jdbcType="INTEGER" property="pid"/>
  </resultMap>
   <select id="getByUserId2" resultMap="BaseMap">
    select * from user
    where user_id = #{userId}
  </select>
  <select id="getPermitsByUserId" resultMap="PermitBaseMap">
    select p.*
    from user_permit up
    inner join permit p on up.permit_id = p.id
    where up.user_id = #{userId}
  </select>

通過fetchType=lazy指定子查詢getPermitsByUserId使用懶載入,這樣的話就不用管全域性設定lazyLoadingEnabled是true還是false了。當然這裡可以直接用多表關聯查詢不使用子查詢,使用方法在這篇文章

測試程式碼

public class Test {
  public static void main(String[] args) throws IOException {
    try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml")) {
      // 構建session工廠 DefaultSqlSessionFactory
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      SqlSession sqlSession = sqlSessionFactory.openSession();
      UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
      UserDO userDO = userMapper.getByUserId2(1);
      System.out.println(userDO);
    }
  }
}

結果如下,打了斷點可以看到原userDO物件已被代理並且permitDOList是null需要呼叫get方法才會去查詢拿到值,咳咳這邊之前直接執行顯示是已經把permitDOList查詢出來了,想了半天啥原因後來才發現println會呼叫userDO物件的toString方法,而toString方法也會走代理方法直接去呼叫子查詢的

延遲載入的好處

延遲載入主要能解決mybatis的N+1問題,什麼是N+1問題其實叫1+N更為合理,以上面的業務例子來說就是假設一次查詢出來10000個使用者,那麼還需要針對這10000個使用者使用子查詢getPermitsByUserId獲取每個使用者的許可權列表,需要10000次查詢,總共10001次,真實情況下你可能並不需要每個子查詢的結果,這樣就浪費資料庫連線資源了。如果使用延遲載入的話就相當於不用進行這10000次查詢,因為它是等到你真正使用的時候才會呼叫子查詢獲取結果。

到此這篇關於MyBatis延遲載入策略深入探究的文章就介紹到這了,更多相關MyBatis延遲載入內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com