Mybatis如何實現關聯屬性懶加載
Mybatis 關聯屬性懶加載
延遲加載配置
mybatis默認沒有開啟延遲加載,需要在config.xml中setting配置。
lazyLoadingEnabled:true使用延遲加載,false禁用延遲加載,默認為false。
aggressiveLazyLoading:true啟用時,當延遲加載開啟時訪問對象中一個懶對象屬性時,將完全加載這個對象的所有懶對象屬性。false,當延遲加載時,按需加載對象屬性(即訪問對象中一個對象屬性時,不會加載對象中的引用屬性)。默認為true。
修改延遲加載需要的select語句
延遲加載場景分析
查詢訂單時,需要關聯查詢訂單明細表
假設有10 個訂單,每個訂單有5個明細信息。
如果用戶僅僅需要查看訂單信息,不需要訂單明細信息,但是查詢瞭10+50個對象到內存(隻有10個是用戶想要的)。顯然是增加內存的負擔。
如果用戶的需求僅僅是查詢訂單信息,我僅僅訂單信息用戶,沒有查詢訂單的詳情,那麼,內存中隻有10對象(10個對象全是客戶想的),這樣做顯然 節省瞭內存。
業務需求特點:需要的時候在進行查詢,不需要的時候不盡興查詢,這種情況就是延遲加載,也叫作懶加載。
配置過程
去掉sql語句中對user表的查詢語句
對resultMap orderAndUser 進行修改
註意:查詢訂單時要同時查詢userid,避免查詢user時沒有憑據
修改orderMapper.xml文件select語句
<!-- 關聯屬性延遲加載 --> <select id="queryOrderAndUser" resultMap="orderAndUser"> select o.id oids,o.orderid,o.createtime,o.note,o.userid from orders o,t_user u where o.userid=u.id </select> <resultMap type="orders" id="orderAndUser"> <id property="id" column="oids"></id> <result property="orderId" column="orderid"></result> <result property="createtime" column="createtime" /> <result property="note" column="note" /> <association property="user" javaType="users" select="net.neuedu.mybatis.mapper.UserMapper.queryUserById" column="userid"> <!-- 這裡不查詢用戶的信息,而是將用的信息放在另外一個查詢中 column是order表中的userid字段 --> </association> </resultMap>
因為在assocication多瞭一個select元素,就要在UserMapper中多加一個方法。
//根據ID查詢用戶 public Users queryUserById(Integer id);
在UserMapper.xml中添加queryUserById的select
<!-- public Users queryUserById(Integer id); --> <select id="queryUserById" parameterType="int" resultType="users"> select * from t_user where id=#{id} </select>
Mybatis註解方式懶加載失效分析
本人在使用spring mvc + mybatis的後臺結構的項目的時候,在使用mybatis的懶加載出現瞭一些問題:
明明懶加載的配置都正確瞭,但就是用debug斷點調試的時候懶加載的屬性總是提前加載,在經過幾天的不斷努力之後,終於發現瞭原因:
mybatis懶加載配置:
<!-- 查詢時,關閉關聯對象即時加載以提高性能 --> <setting name="lazyLoadingEnabled" value="true" /> <!-- 設置關聯對象加載的形態,此處為按需加載字段(加載字段由SQL指 定),不會加載關聯表的所有字段,以提高性能 --> <setting name="aggressiveLazyLoading" value="false" />
懶加載查詢語句:
@Select("select * from user_main where username=#{username}") @Results({ @Result(property = "roleNames", column = "id", many = @Many(fetchType=FetchType.LAZY,select = "getRoleNamesByUserId")), @Result(property = "permissionNames", column = "id", many = @Many(fetchType=FetchType.LAZY,select = "getPermissionsByUserId")) }) public UserPO getUserByUsername(@Param("username")String username);
測試用例:
@Test public void testGetUserByUsername() throws Exception { SqlSession session = sqlSessionFactory.openSession(); try { UserMapper userMapper = session.getMapper(UserMapper.class); UserPO userPo = userMapper.getUserByUsername("zhangsan"); System.out.println(userPo); System.out.println(userPo.getPermissionNames()); // List<String> map = userMapper.getRoleMain(); // System.out.println(map); session.close(); } finally { session.close(); } }
結果調試測試用例的時候,使用debug卻發現懶加載並沒有生效,後來看瞭mybatis懶加載的源碼,依然沒有發現原因,感覺就是roleNames和permissionNames就是突然的加載瞭,斷點也捕捉不到,也沒有博捉到觸發的事件,這時樓主我就很奇怪瞭,最後經過幾天的努力終於發現瞭原因:
mybatis源碼:configuration
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
默認這四個方法或觸發懶加載,當然,觸發懶加載的方法還有getRoleNames 和 getPermissions ,這兩個(隻針對當前實例)
然後通過sysout輸出的時候,發現hashcode和equals方法被莫名的觸發瞭,而且debug斷點還捕捉不到,然後roleNames和permissionNames就被莫名的加載瞭
最後發現,這個問題和debug的原理有一定的關系,
原來,在顯示debug的黃色框框時,debug會另起一個線程,然後重新調用一遍代碼,然後顯示userpo信息的時候,又會調用userpo的hashcode方法和tostring方法,
這樣的話,就會導致這兩個方法會在debug線程內觸發懶加載,造成的效果就是懶加載失效,但是實際上懶加載是生效瞭的,隻是在debug模式上被觸發瞭,而且用斷點還捕捉不到,就會形成一個奇怪的問題,如果想用debug來查看效果,也是很簡單:
通過將觸發默認的四個方法屏蔽來查看效果,但是不支持,因為這樣的話,可能會影響實體的hash排序等問題,即使是用瞭這個方法,也是建議看完效果以後改成默認的
這種事代碼方式的,還有一種是配置文件方式的(自己從網上找)
configuration.setLazyLoadTriggerMethods(new HashSet<String>());
還有一種更好的方式來查看懶加載的效果,那就是mybatis的默認日志,建議將日志級別調成debug(這個debug級別和debug模式不一樣),然後執行的時候就可以通過sql語句的打印來判斷懶加載是否生效。
其實這個問題的分析最後,還是在說debug斷點調試的問題,它的這種原理需要註意,特別是會在另一個線程調用hashcode和tostring方法,相信在很多方面會影響到我們,希望大傢能夠註意吧~
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- MyBatis延遲加載策略深入探究
- Mybatis加載策略的實現方法
- mybatis中註解與xml配置的對應關系和對比分析
- Mybatis結果集映射與生命周期詳細介紹
- mybatis映射和實際類型不一致的問題