- 通過前面的學習,我們已經掌握了 Mybatis 中一對一,一對多,多對多關係的配置及實現,可以實現對象的
關聯查詢。實際開發過程中很多時候我們並不需要總是在加載用戶信息時就一定要加載他的賬戶信息。此時就是我們所說的延遲加載。
- 何爲延遲加載?
- 延遲加載:
就是在需要用到數據時才進行加載,不需要用到數據時就不加載數據。延遲加載也稱懶加載. - 好處:先從單表查詢,需要時再從關聯表去關聯查詢,大大提高數據庫性能,因爲查詢單表要比關聯查詢多張錶速度要快。
- 壞處:
因爲只有當需要用到數據時,纔會進行數據庫查詢,這樣在大批量數據查詢時,因爲查詢工作也要消耗時間,所以可能造成用戶等待時間變長,造成用戶體驗下降。
- 實現需求
- 需求:
查詢賬戶(Account)信息並且關聯查詢用戶(User)信息。如果先查詢賬戶(Account)信息即可滿足要求,當我們需要查詢用戶(User)信息時再查詢用戶(User)信息。把對用戶(User)信息的按需去查詢就是延遲加載。
我們使用了resultMap來實現一對一,一對多,多對多關係的操作。主要是通過 association、collection 實現一對一及一對多映射。association、collection 具備延遲加載功能。
使用 assocation 實現延遲加載
- 需求:
查詢賬戶信息同時查詢用戶信息。 - 賬戶的持久層 DAO 接口
public interface IAccountDao {
/**
* 查詢所有賬戶,同時獲取賬戶的所屬用戶名稱以及它的地址信息
* @return
*/
List<Account> findAll();
}
- 賬戶的持久層映射文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IAccountDao">
<!-- 建立對應關係 -->
<resultMap type="account" id="accountMap">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
<!-- 它是用於指定從表方的引用實體屬性的 -->
<association property="user" javaType="user"
select="com.itheima.dao.IUserDao.findById" column="uid">
</association>
</resultMap>
<select id="findAll" resultMap="accountMap">
select * from account
</select>
</mapper>
select: 填寫我們要調用的 select 映射的 id
column : 填寫我們要傳遞給 select 映射的參數
- 用戶的持久層接口和映射文件
public interface IUserDao {
/**
* 根據 id 查詢
* @param userId
* @return
*/
User findById(Integer userId);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IUserDao">
<!-- 根據 id 查詢 -->
<select id="findById" resultType="user" parameterType="int" >
select * from user where id = #{uid}
</select>
</mapper>
- 開啓 Mybatis 的延遲加載策略
我們需要在 Mybatis 的配置文件 SqlMapConfig.xml 文件中添加延遲加載的配置。
<!-- 開啓延遲加載的支持 -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
- 編寫測試只查賬戶信息不查用戶信息。
public class AccountTest {
private InputStream in ;
private SqlSessionFactory factory;
private SqlSession session;
private IAccountDao accountDao;
@Test
public void testFindAll() {
//6.執行操作
List<Account> accounts = accountDao.findAll();
}
@Before
//在測試方法執行之前執行
public void init()throws Exception {
//1.讀取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.創建構建者對象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.創建 SqlSession 工廠對象
factory = builder.build(in);
//4.創建 SqlSession 對象
session = factory.openSession();
//5.創建 Dao 的代理對象
accountDao = session.getMapper(IAccountDao.class);
}
@After
//在測試方法執行完成之後執行
public void destroy() throws Exception{
//7.釋放資源
session.close();
in.close();
}
}
- 測試結果如下:
我們發現,因爲本次只是將 Account對象查詢出來放入 List 集合中,並沒有涉及到 User對象,所以就沒有發出 SQL 語句查詢賬戶所關聯的 User 對象的查詢。
使用 Collection 實現延遲加載
同樣我們也可以在一對多關係配置的< collection >結點中配置延遲加載策略。
< collection >結點中也有 select 屬性,column 屬性。
需求:
完成加載用戶對象時,查詢該用戶所擁有的賬戶信息。
- 在 User 實體類中加入 List< Account >屬性
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ",
birthday=" + birthday + ", sex=" + sex + ", address="
+ address + "]";
}
}
- 編寫用戶和賬戶持久層接口的方法
/**
* 查詢所有用戶,同時獲取出每個用戶下的所有賬戶信息
* @return
*/
List<User> findAll();
/**
* 根據用戶 id 查詢賬戶信息
* @param uid
* @return
*/
List<Account> findByUid(Integer uid);
- 編寫用戶持久層映射配置
<resultMap type="user" id="userMap">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!-- collection 是用於建立一對多中集合屬性的對應關係
ofType 用於指定集合元素的數據類型
select 是用於指定查詢賬戶的唯一標識(賬戶的 dao 全限定類名加上方法名稱)
column 是用於指定使用哪個字段的值作爲條件查詢
-->
<collection property="accounts" ofType="account"
select="com.itheima.dao.IAccountDao.findByUid" column="id">
</collection>
</resultMap>
<!-- 配置查詢所有操作 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
<collection>標籤:
主要用於加載關聯的集合對象
select 屬性:
用於指定查詢 account 列表的 sql 語句,所以填寫的是該 sql 映射的 id
column 屬性:
用於指定 select 屬性的 sql 語句的參數來源,上面的參數來自於 user 的 id 列,
所以就寫成 id 這一個字段名了
- 編寫賬戶持久層映射配置
<!-- 根據用戶 id 查詢賬戶信息 -->
<select id="findByUid" resultType="account" parameterType="int">
select * from account where uid = #{uid}
</select>
- 測試只加載用戶信息
public class UserTest {
private InputStream in ;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Test
public void testFindAll() {
//6.執行操作
List<User> users = userDao.findAll();
}
@Before
//在測試方法執行之前執行
public void init() throws Exception {
//1.讀取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.創建構建者對象
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//3.創建 SqlSession 工廠對象
factory = builder.build(in);
//4.創建 SqlSession 對象
session = factory.openSession();
//5.創建 Dao 的代理對象
userDao = session.getMapper(IUserDao.class);
}
@After
//在測試方法執行完成之後執行
public void destroy() throws Exception{
session.commit();
//7.釋放資源
session.close();
in.close();
}
}
測試結果如下:
我們發現並沒有加載 Account 賬戶信息。