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


  首先我們先思考一個問題,假設:在一對多中,我們有一個用戶,他有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循環打印查詢的數據,使用數據,這時候因為使用了數據所以將查詢賬戶信息,我們發現用戶和賬戶查詢都進行了執行


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM