Mybatis 學習筆記(六)——延遲載入
一、延遲載入介紹
延遲載入的目的是為了加快查詢速度,提升資料庫效能。對於一個複雜的查詢sql,在業務許可的情況下,我們可以用兩種方式來提升查詢速度(Mybatis環境),讓資料庫的效能蹭蹭的往上提升。第一種是將這個複查查詢分成兩個 statement 先執行其中一個,然後根據需求在 Service 中呼叫執行另一個 statement ;第二種是通過延遲載入的方式(按需載入)。下面重點介紹通過延遲載入的方式實現資料庫效能的提升。
二、延遲載入的實現
這裡以查詢訂單的使用者資訊為例。
(一)資料庫結構
CREATE TABLE `orders` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '下單使用者id', `number` varchar(32) NOT NULL COMMENT '訂單號', `createtime` datetime NOT NULL COMMENT '建立訂單時間', `note` varchar(100) DEFAULT NULL COMMENT '備註', PRIMARY KEY (`id`), KEY `FK_orders_1` (`user_id`), CONSTRAINT `FK_orders_id` FOREIGN KEY (`user_id`) REFERENCES `user` (`ID`) ON DELETE NO ACTION ON UPDATE NO ACTION ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; CREATE TABLE `user` ( `ID` int(10) NOT NULL AUTO_INCREMENT, `USERNAME` varchar(255) DEFAULT NULL, `SEX` varchar(255) DEFAULT NULL, `birthday` date DEFAULT NULL, `address` varchar(255) DEFAULT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8mb4;
(二)編寫POJO類
/mybatis01/src/com/po/Orders.java
package com.po; import java.util.Date; import java.util.List; /** * 訂單類 * @author 歐陽 * */ public class Orders { private Integer id; //id private Integer userId; //使用者id private String number; //數量 private Date createtime; //建立時間 private String note; //備註 private User user; //使用者資訊 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number == null ? null : number.trim(); } public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } public String getNote() { return note; } public void setNote(String note) { this.note = note == null ? null : note.trim(); } public User getUser() { return user; } public void setUser(User user) { this.user = user; } }
/mybatis01/src/com/po/User.java
package com.po; import java.util.Date; import java.util.List; /** * 使用者類 * @author 歐陽 * */ public class User { private int id; //id private String username; //使用者名稱 private String sex; //性別 private Date birthday; //生日 private String address; //地址 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Date getBirthday() { return birthday; } public void setBirthday(Date birthday) { this.birthday = birthday; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
(三)編寫Mapper檔案
/mybatis01/src/config/mapper/OrdersCustomMapper.xml
<!--
延遲載入 :延遲載入的目的是為了提高查詢速度,提升資料庫效能
需求:查詢訂單的使用者資訊
只有resultMap中的association和collection才有延遲載入功能
-->
<resultMap type="com.po.Orders" id="OrdersUserLazyLoading">
<!-- 配置對映的訂單資訊 -->
<id column="id" property="id"/>
<result column="user_id" property="userId"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!--
select:指定延遲載入需要執行的statement的id(是根據userId查詢使用者資訊的statement)
column:Orders訂單資訊中關聯User使用者資訊查詢的列,是user_id
fetchType="lazy":啟用延時載入,可在SqlMapConfig.xml中setting中配置全域性的
-->
<association property="user" select="findUserById"
column="user_id" fetchType="lazy">
</association>
</resultMap>
<!-- 查詢訂單資訊 -->
<select id="findOrdersUserLazyLoading" resultMap="OrdersUserLazyLoading">
select * from orders
</select>
<!-- 根據使用者id查詢使用者資訊 -->
<select id="findUserById" parameterType="int" resultType="user">
select * from user where id = #{id}
</select>
在上述程式碼中的 association 的屬性 fetchType="lazy"
設定後才啟動延時載入,也可不在此設定,而在配置檔案中設定全域性引數 lazyLoadingEnabled
和 aggressiveLazyLoading
,,但你會發現只設置 lazyLoadingEnabled 也好使,但是我要說的是,如果這樣設定可能會帶來一想不到的問題,所以兩個都要設定。
<!-- 全域性配置引數 -->
<settings>
<!-- 全域性性設定懶載入 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--
當設定為‘true’的時候,懶載入的物件可能被任何懶屬性全部載入。
否則,每個屬性都按需載入。
-->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
(四)編寫Mapper介面
/mybatis01/src/com/mapper/OrdersCustomMapper.java
/**
* 延遲載入,查詢訂單資訊,關聯查詢建立訂單的使用者資訊
*/
public List<Orders> findOrdersUserLazyLoading() throws Exception;
(五)JUnit 測試
/mybatis01/test/com/mapper/OrdersCustomMapperTest.java
@Test
public void testFindOrdersUserLazyLoading() {
SqlSession sqlsession = sqlSessionFactory.openSession();
OrdersCustomMapper mapper = sqlsession
.getMapper(OrdersCustomMapper.class);
try {
List<Orders> orders = mapper.findOrdersUserLazyLoading();
//對orders進行遍歷
for(Orders entry : orders) {
//在呼叫entry.getUser() 時 去查詢使用者資訊,這裡是按需載入(延遲載入)
System.out.println(entry.getUser().getUsername());
}
} catch (Exception e) {
e.printStackTrace();
}
}
(六)測試結果驗證
當沒有註釋System.out.println(entry.getUser().getUsername());
時,日誌輸出如下:
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 2100440237.
DEBUG [main] - Setting autocommit to false on JDBC Connection [[email protected]]
DEBUG [main] - ==> Preparing: select * from orders
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 3
DEBUG [main] - ==> Preparing: select * from user where id = ?
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <== Total: 1
張三
張三
DEBUG [main] - ==> Preparing: select * from user where id = ?
DEBUG [main] - ==> Parameters: 10(Integer)
DEBUG [main] - <== Total: 1
測試
而註釋System.out.println(entry.getUser().getUsername());
後,日誌輸出如下:
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 2100440237.
DEBUG [main] - Setting autocommit to false on JDBC Connection [[email protected]]
DEBUG [main] - ==> Preparing: select * from orders
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 3
根據這兩次的日誌輸出情況可以看出,查詢訂單的使用者資訊是延遲載入的(按需載入)。