Mybatis如何實(shí)現(xiàn)關(guān)聯(lián)屬性懶加載
Mybatis 關(guān)聯(lián)屬性懶加載
延遲加載配置
mybatis默認(rèn)沒(méi)有開(kāi)啟延遲加載,需要在config.xml中setting配置。
lazyLoadingEnabled:true使用延遲加載,false禁用延遲加載,默認(rèn)為false。
aggressiveLazyLoading:true啟用時(shí),當(dāng)延遲加載開(kāi)啟時(shí)訪問(wèn)對(duì)象中一個(gè)懶對(duì)象屬性時(shí),將完全加載這個(gè)對(duì)象的所有懶對(duì)象屬性。false,當(dāng)延遲加載時(shí),按需加載對(duì)象屬性(即訪問(wèn)對(duì)象中一個(gè)對(duì)象屬性時(shí),不會(huì)加載對(duì)象中的引用屬性)。默認(rèn)為true。
修改延遲加載需要的select語(yǔ)句
延遲加載場(chǎng)景分析
查詢(xún)訂單時(shí),需要關(guān)聯(lián)查詢(xún)訂單明細(xì)表
假設(shè)有10 個(gè)訂單,每個(gè)訂單有5個(gè)明細(xì)信息。
如果用戶(hù)僅僅需要查看訂單信息,不需要訂單明細(xì)信息,但是查詢(xún)了10+50個(gè)對(duì)象到內(nèi)存(只有10個(gè)是用戶(hù)想要的)。顯然是增加內(nèi)存的負(fù)擔(dān)。
如果用戶(hù)的需求僅僅是查詢(xún)訂單信息,我僅僅訂單信息用戶(hù),沒(méi)有查詢(xún)訂單的詳情,那么,內(nèi)存中只有10對(duì)象(10個(gè)對(duì)象全是客戶(hù)想的),這樣做顯然 節(jié)省了內(nèi)存。
業(yè)務(wù)需求特點(diǎn):需要的時(shí)候在進(jìn)行查詢(xún),不需要的時(shí)候不盡興查詢(xún),這種情況就是延遲加載,也叫作懶加載。
配置過(guò)程
去掉sql語(yǔ)句中對(duì)user表的查詢(xún)語(yǔ)句
對(duì)resultMap orderAndUser 進(jìn)行修改
注意:查詢(xún)訂單時(shí)要同時(shí)查詢(xún)userid,避免查詢(xún)user時(shí)沒(méi)有憑據(jù)
修改orderMapper.xml文件select語(yǔ)句
<!-- 關(guān)聯(lián)屬性延遲加載 --> <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"> <!-- 這里不查詢(xún)用戶(hù)的信息,而是將用的信息放在另外一個(gè)查詢(xún)中 column是order表中的userid字段 --> </association> </resultMap>
因?yàn)樵赼ssocication多了一個(gè)select元素,就要在UserMapper中多加一個(gè)方法。
//根據(jù)ID查詢(xún)用戶(hù) 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的后臺(tái)結(jié)構(gòu)的項(xiàng)目的時(shí)候,在使用mybatis的懶加載出現(xiàn)了一些問(wèn)題:
明明懶加載的配置都正確了,但就是用debug斷點(diǎn)調(diào)試的時(shí)候懶加載的屬性總是提前加載,在經(jīng)過(guò)幾天的不斷努力之后,終于發(fā)現(xiàn)了原因:
mybatis懶加載配置:
<!-- 查詢(xún)時(shí),關(guān)閉關(guān)聯(lián)對(duì)象即時(shí)加載以提高性能 --> <setting name="lazyLoadingEnabled" value="true" /> <!-- 設(shè)置關(guān)聯(lián)對(duì)象加載的形態(tài),此處為按需加載字段(加載字段由SQL指 定),不會(huì)加載關(guān)聯(lián)表的所有字段,以提高性能 --> <setting name="aggressiveLazyLoading" value="false" />
懶加載查詢(xún)語(yǔ)句:
@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);
測(cè)試用例:
@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(); } }
結(jié)果調(diào)試測(cè)試用例的時(shí)候,使用debug卻發(fā)現(xiàn)懶加載并沒(méi)有生效,后來(lái)看了mybatis懶加載的源碼,依然沒(méi)有發(fā)現(xiàn)原因,感覺(jué)就是roleNames和permissionNames就是突然的加載了,斷點(diǎn)也捕捉不到,也沒(méi)有博捉到觸發(fā)的事件,這時(shí)樓主我就很奇怪了,最后經(jīng)過(guò)幾天的努力終于發(fā)現(xiàn)了原因:
mybatis源碼:configuration
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
默認(rèn)這四個(gè)方法或觸發(fā)懶加載,當(dāng)然,觸發(fā)懶加載的方法還有g(shù)etRoleNames 和 getPermissions ,這兩個(gè)(只針對(duì)當(dāng)前實(shí)例)
然后通過(guò)sysout輸出的時(shí)候,發(fā)現(xiàn)hashcode和equals方法被莫名的觸發(fā)了,而且debug斷點(diǎn)還捕捉不到,然后roleNames和permissionNames就被莫名的加載了
最后發(fā)現(xiàn),這個(gè)問(wèn)題和debug的原理有一定的關(guān)系,
原來(lái),在顯示debug的黃色框框時(shí),debug會(huì)另起一個(gè)線程,然后重新調(diào)用一遍代碼,然后顯示userpo信息的時(shí)候,又會(huì)調(diào)用userpo的hashcode方法和tostring方法,
這樣的話(huà),就會(huì)導(dǎo)致這兩個(gè)方法會(huì)在debug線程內(nèi)觸發(fā)懶加載,造成的效果就是懶加載失效,但是實(shí)際上懶加載是生效了的,只是在debug模式上被觸發(fā)了,而且用斷點(diǎn)還捕捉不到,就會(huì)形成一個(gè)奇怪的問(wèn)題,如果想用debug來(lái)查看效果,也是很簡(jiǎn)單:
通過(guò)將觸發(fā)默認(rèn)的四個(gè)方法屏蔽來(lái)查看效果,但是不支持,因?yàn)檫@樣的話(huà),可能會(huì)影響實(shí)體的hash排序等問(wèn)題,即使是用了這個(gè)方法,也是建議看完效果以后改成默認(rèn)的
這種事代碼方式的,還有一種是配置文件方式的(自己從網(wǎng)上找)
configuration.setLazyLoadTriggerMethods(new HashSet<String>());
還有一種更好的方式來(lái)查看懶加載的效果,那就是mybatis的默認(rèn)日志,建議將日志級(jí)別調(diào)成debug(這個(gè)debug級(jí)別和debug模式不一樣),然后執(zhí)行的時(shí)候就可以通過(guò)sql語(yǔ)句的打印來(lái)判斷懶加載是否生效。
其實(shí)這個(gè)問(wèn)題的分析最后,還是在說(shuō)debug斷點(diǎn)調(diào)試的問(wèn)題,它的這種原理需要注意,特別是會(huì)在另一個(gè)線程調(diào)用hashcode和tostring方法,相信在很多方面會(huì)影響到我們,希望大家能夠注意吧~
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
struts2實(shí)現(xiàn)簡(jiǎn)單文件下載功能
這篇文章主要為大家詳細(xì)介紹了struts2實(shí)現(xiàn)簡(jiǎn)單文件下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01Java中FileOutputStream類(lèi)的使用
java.io.FileOutputStream類(lèi)是文件輸出流,用于將數(shù)據(jù)寫(xiě)出到文件,下面就來(lái)介紹一下Java中FileOutputStream類(lèi)的使用,具有一定的參考價(jià)值,感興趣的可以了解一下2023-10-10詳解Spring Cloud Hystrix斷路器實(shí)現(xiàn)容錯(cuò)和降級(jí)
本篇文章主要介紹了詳解Spring Cloud Hystrix斷路器實(shí)現(xiàn)容錯(cuò)和降級(jí),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05Java 垃圾回收機(jī)制詳解(動(dòng)力節(jié)點(diǎn)Java學(xué)院整理)
在系統(tǒng)運(yùn)行過(guò)程中,會(huì)產(chǎn)生一些無(wú)用的對(duì)象,這些對(duì)象占據(jù)著一定的內(nèi)存,如果不對(duì)這些對(duì)象清理回收無(wú)用對(duì)象的內(nèi)存,可能會(huì)導(dǎo)致內(nèi)存的耗盡,所以垃圾回收機(jī)制回收的是內(nèi)存。下面通過(guò)本文給大家詳細(xì)介紹java垃圾回收機(jī)制,一起學(xué)習(xí)吧2017-02-02