Mybatis延遲載入的實現以及使用場景

  • 2019 年 10 月 3 日
  • 筆記

  首先我們先思考一個問題,假設:在一對多中,我們有一個用戶,他有100個賬戶。

  問題1:在查詢用戶的時候,要不要把關聯的賬戶查出來?

  問題2:在查詢賬戶的時候,要不要把關聯的用戶查出來?

  解答:在查詢用戶的時候,用戶下的賬戶資訊應該是我們什麼時候使用,什麼時候去查詢。

     在查詢賬戶的時候,賬戶的所屬用戶資訊應該是隨著賬戶查詢時一起查詢出來。

  搞清楚這兩個簡單的問題後,我們就可以引出延遲載入和立即載入的特性。

  延遲載入:在真正使用數據的時候才發起查詢,不用的時候不查詢關聯的數據,延遲載入又叫按需查詢(懶載入)

  立即載入:不管用不用,只要一調用方法,馬上發起查詢。

  使用場景:在對應的四種表關係中,一對多、多對多通常情況下採用延遲載入,多對一、一對一通常情況下採用立即載入。

  理解了延遲載入的特性以後再看Mybatis中如何實現查詢方法的延遲載入,在MyBatis 的配置文件中通過設置settings的lazyLoadingEnabled屬性為true進行開啟全局的延遲載入,通過aggressiveLazyLoading屬性開啟立即載入。看一下官網的介紹,然後通過一個實例來實現Mybatis的延遲載入,在例子中我們展現一對多表關係情況下,通過實現查詢用戶資訊同時查詢出該用戶所擁有的賬戶資訊的功能展示一下延遲載入的實現方式以及延遲載入和立即載入的結果的不同之處。

lazyLoadingEnabled 延遲載入的全局開關。當開啟時,所有關聯對象都會延遲載入。 特定關聯關係中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。 true | false false
aggressiveLazyLoading 當開啟時,任何方法的調用都會載入該對象的所有屬性。 否則,每個屬性會按需載入(參考 lazyLoadTriggerMethods)。 true | false false (在 3.4.1 及之前的版本默認值為 true)

  

 

  1.用戶類以及賬戶類

public class User implements Serializable{      private Integer id;      private String username;      private Date birthday;      private String sex;      private String address;      private List<Account> accountList;        get和set方法省略.....  }      public class Account implements Serializable{      private Integer id;      private Integer uid;      private Double money;        get和set方法省略.....  }

注意因為我們是查找用戶的同時查找出其所擁有的賬戶所以我們需要在用戶類中增加賬戶的集合的屬性,用來封裝返回的結果。

  2.在UserDao介面中聲明findAll方法

/**       * 查詢所有的用戶       *       * @return       */      List<User> findAll();

  3.在UserDao.xml中配置findAll方法的映射

<resultMap id="userAccountMap" type="com.example.domain.User">          <id property="id" column="id"/>          <result property="username" column="username"/>          <result property="birthday" column="birthday"/>          <result property="sex" column="sex"/>          <result property="address" column="address"/>          <collection property="accountList" ofType="com.example.domain.Account" column="id"                      select="com.example.dao.AccountDao.findAllByUid"/>      </resultMap>      <select id="findAll" resultMap="userAccountMap">          SELECT * FROM USER;      </select>

  主要的功能實現位於 <collection property="accountList" ofType="com.example.domain.Account" column="id" select="com.example.dao.AccountDao.findAllByUid"/>中,對於賬戶列表的資訊通過collection集合來映射,通過select指定集合中的每個元素如何查詢,在本例中select的屬性值為AccountDao.xml文件的namespace   com.example.dao.AccountDao路徑以及指定該映射文件下的findAllByUid方法,通過這個唯一標識指定集合中元素的查找方式。因為在這裡需要用到根據用戶ID查找賬戶,所以需要同時配置一下findAllByUid方法的實現。

  4.配置collection中select屬性所使用的方法 findAllByUid

AccountDao介面中添加   /**       * 根據用戶ID查詢賬戶資訊       * @return       */      List<Account> findAllByUid(Integer uid);    AccountDao.xml文件中配置  <select id="findAllByUid" resultType="com.example.domain.Account">          SELECT * FROM account WHERE uid = #{uid};      </select>

  5.在Mybatis的配置文件中開啟全局延遲載入

configuration>      <settings>          <!--開啟全局的懶載入-->          <setting name="lazyLoadingEnabled" value="true"/>          <!--關閉立即載入,其實不用配置,默認為false-->          <setting name="aggressiveLazyLoading" value="false"/>          <!--開啟Mybatis的sql執行相關資訊列印-->          <setting name="logImpl" value="STDOUT_LOGGING" />      </settings>      <typeAliases>          <typeAlias type="com.example.domain.Account" alias="account"/>          <typeAlias type="com.example.domain.User" alias="user"/>          <package name="com.example.domain"/>      </typeAliases>      <environments default="test">          <environment id="test">              <!--配置事務-->              <transactionManager type="jdbc"></transactionManager>              <!--配置連接池-->              <dataSource type="POOLED">                  <property name="driver" value="com.mysql.jdbc.Driver"/>                  <property name="url" value="jdbc:mysql://localhost:3306/test1"/>                  <property name="username" value="root"/>                  <property name="password" value="123456"/>              </dataSource>          </environment>      </environments>      <!--配置映射文件的路徑-->      <mappers>          <mapper resource="com/example/dao/UserDao.xml"/>          <mapper resource="com/example/dao/AccountDao.xml"/>      </mappers>  </configuration>

  6.測試方法

    private InputStream in;      private SqlSession session;        private UserDao userDao;      private AccountDao accountDao;      private SqlSessionFactory factory;      @Before      public void init()throws Exception{          //獲取配置文件          in = Resources.getResourceAsStream("SqlMapConfig.xml");          //獲取工廠          factory = new SqlSessionFactoryBuilder().build(in);            session = factory.openSession();            userDao = session.getMapper(UserDao.class);          accountDao = session.getMapper(AccountDao.class);      }      @After      public void destory()throws Exception{          session.commit();          session.close();          in.close();      }      @Test      public void findAllTest(){          List<User> userList = userDao.findAll();  //        for (User user: userList){  //            System.out.println("每個用戶的資訊");  //            System.out.println(user);  //            System.out.println(user.getAccountList());  //        }      }

測試說明:當我們注釋了findAllTest()方法中的for循環列印的時候,我們將不會需要用戶的賬戶資訊,按照延遲載入的特性程式只會查詢用戶的資訊,而不會查詢賬戶的資訊。當我我們放開for循環列印的時候我們使用到了用戶和賬戶的資訊,程式會同時將用戶以及對應的賬戶資訊列印出來。

  7.測試結果

  (1)注釋for循環,不使用數據,這時候不需要查詢賬戶資訊,我們能在控制台中看到sql執行結果是因為在Mybatis配置文件中添加了logImpl的配置,具體參考第5步中的配置資訊

    (2)通過for循環列印查詢的數據,使用數據,這時候因為使用了數據所以將查詢賬戶資訊,我們發現用戶和賬戶查詢都進行了執行

Tags: