標籤:聲明 簡單 end pre bug 值傳遞 type屬性 std ack
聲明:面試是遇到消極式載入問題,在網頁搜尋到此篇文章,感覺很有協助,留此學習之用!
一、分步查詢
分步查詢通常應用於關聯表查詢,如:電商平台,查詢訂單資訊時需要查詢部分的使用者資訊;OA系統查詢個人資訊時需要查詢部門資訊,反之亦是。相對於關聯查詢來說,分步查詢將查詢sql拆分,這裡引申出一個問題是:分步查詢與關聯表查詢的不同。
從代碼層面來說:關聯表查詢能夠有效簡化代碼編寫邏輯,減小代碼編寫難度,同時避免B-U-G(代碼多了,bug 就多了);
而分步查詢則能夠增強代碼的可用性(這點我也不是非常理解,在實際開發中也未遇到過)
從功能上說:關聯表查詢畢竟只需要查詢一次資料庫,對於業務量較小的系統來說,效率更高,資料庫壓 力相對較小;
分步查詢雖然需要多次查詢資料,但是這也意味著能夠更好地使用資料快取服務,且緩衝的 資料耦合度低,利用率高,而且單次查詢效率很高,資料庫壓力較小(對於業務量較大的系 統來說)。還有一點則是資料庫鎖的問題,畢竟關聯查詢是多表同時使用,分步查詢每次只 操作一個表。
分步查詢的實現(以簡單的OA系統為例)
業務情境一:嵌套標籤association的應用---查詢員工資訊時,擷取員工所在的部門資訊。
1.建立員工與部門實體類
/** * 員工實體類 * * @author xuyong * */public class Employee { private Integer id;private String lastName;private String gender;private String email;private Department department;private Integer dId; public Integer getdId() {return dId;} public void setdId(Integer dId) {this.dId = dId;} public Integer getId() {return id;} public void setId(Integer id) {this.id = id;} public String getLastName() {return lastName;} public void setLastName(String lastName) {this.lastName = lastName;} public String getGender() {return gender;} public void setGender(String gender) {this.gender = gender;} public String getEmail() {return email;} public void setEmail(String email) {this.email = email;} public Department getDepartment() {return department;} public void setDepartment(Department department) {this.department = department;} @Overridepublic String toString() {return "Employee [dId=" + dId + ", department=" + department+ ", email=" + email + ", gender=" + gender + ", id=" + id+ ", lastName=" + lastName + "]";}}
/** * 部門實體類 * * @author xuyong * */public class Department { private Integer id;private String departmentName;private List<Employee> employee; public List<Employee> getEmployee() {return employee;} public void setEmployee(List<Employee> employee) {this.employee = employee;} /** * */private Department() {super();// TODO Auto-generated constructor stub} /** * @param id * @param departmentName */private Department(Integer id, String departmentName) {super();this.id = id;this.departmentName = departmentName;} public Integer getId() {return id;} public void setId(Integer id) {this.id = id;} public String getDepartmentName() {return departmentName;} public void setDepartmentName(String departmentName) {this.departmentName = departmentName;} @Overridepublic String toString() {return "Department [departmentName=" + departmentName + ", employee="+ employee + ", id=" + id + "]";} }
2.JUNIT單元測試編寫
擷取sqlSession執行個體(未使用架構,自己手動擷取)
/** * 擷取sqlSession執行個體 * @return * @throws IOException */public SqlSessionFactory getSessionFactory() throws IOException {String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);return new SqlSessionFactoryBuilder().build(inputStream);}
業務方法
/** * 分布查詢實現: * 擷取員工資訊及部門資訊 * @throws IOException */@Testpublic void test08() throws IOException {SqlSessionFactory sessionFactory = getSessionFactory();SqlSession sqlSession = sessionFactory.openSession();try {EmployeeDaoPlus dao = sqlSession.getMapper(EmployeeDaoPlus.class);Employee employee = dao.getEmpByIdStep(1);System.out.println(employee);} catch (Exception e) {e.printStackTrace();} finally {sqlSession.close();}}
在Mapper中定義介面
/** * 員工類Mapper介面 * * @author xuyong * */public interface EmployeeDaoPlus { /** * 分步查詢測試 * 擷取員工資訊同時擷取部門資訊 * @param id * @return */Employee getEmpByIdStep(Integer id);}
編寫對應的XML(sql資源檔)檔案
<!-- 使用association分布查詢 --> <select id="getEmpByIdStep" resultMap="MyEmpByStep"> select * from employee where id=#{id} </select>
以下為查詢結果,可以看到列印了兩次sql
[com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-==> Preparing: select * from employee where id=? [com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-==> Parameters: 1(Integer) [com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-<== Columns: id, last_name, gender, email, d_id [com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-<== Row: 1, 徐永, 男, [email protected], 1 [com.xuyong.dao.DepartmentDao.getDepartmentById]-====> Preparing: select id,department_name from department where id=? [com.xuyong.dao.DepartmentDao.getDepartmentById]-====> Parameters: 1(Integer) [com.xuyong.dao.DepartmentDao.getDepartmentById]-<==== Columns: id, department_name [com.xuyong.dao.DepartmentDao.getDepartmentById]-<==== Row: 1, 文案部 [com.xuyong.dao.DepartmentDao.getDepartmentById]-<==== Total: 1 [com.xuyong.dao.EmployeeDaoPlus.getEmpByIdStep]-<== Total: 1 Employee [dId=1, department=Department [departmentName=文案部, employee=null, id=1], [email protected], gender=男, id=1, lastName=徐永]
業務情境二:嵌套標籤collection的應用---查詢部門資訊時,擷取該部門下所有的員工。
collection主要用於映射集合資料,工作中常見的應用情境有:
訂單查詢、地址查詢、產品分類查詢
Junit單元測試
/** * 分步查詢 * 查詢部門時擷取部門裡所有的員工資訊 * * @throws IOException */@Testpublic void test10() throws IOException {SqlSessionFactory sessionFactory = getSessionFactory();SqlSession sqlSession = sessionFactory.openSession();try {DepartmentDao dao = sqlSession.getMapper(DepartmentDao.class);Department department = dao.getDeptAndAEmpsByStep(1);/*EmployeeDaoPlus dao2 = sqlSession.getMapper(EmployeeDaoPlus.class);Employee employee = dao2.getEmpById(department.getEmployee().get(0).getId());*/System.out.println(department);} catch (Exception e) {e.printStackTrace();} finally {sqlSession.close();}}
介面定義此處忽略
XML檔案編寫
<!-- collection分布查詢 --> <resultMap type="com.xuyong.entity.Department" id="DepartAndEmpByStep"> <id column="id" property="id"/> <result column="department_name" property="departmentName"/> <collection property="employee" select="com.xuyong.dao.EmployeeDaoPlus.getEmpByDid" column="id"></collection> </resultMap> <!-- collection分布查詢部門下的所有員工資訊 --> <select id="getDeptAndAEmpsByStep" resultMap="DepartAndEmpByStep"> select id,department_name from department where id = #{id} </select>
查詢結果
[com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-==> Preparing: select id,department_name from department where id = ? [com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-==> Parameters: 1(Integer) [com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-<== Columns: id, department_name [com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-<== Row: 1, 文案部 [com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-====> Preparing: select * from employee where d_id = ? [com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-====> Parameters: 1(Integer) [com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-<==== Columns: id, last_name, gender, email, d_id [com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-<==== Row: 1, 徐永, 男, [email protected], 1 [com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-<==== Row: 4, 張三, 女, 123131313, 1 [com.xuyong.dao.EmployeeDaoPlus.getEmpByDid]-<==== Total: 2 [com.xuyong.dao.DepartmentDao.getDeptAndAEmpsByStep]-<== Total: 1 Department [departmentName=文案部, employee=[Employee [dId=1, department=null, emai[email protected], gender=男, id=1, lastName=徐永], Employee [dId=1, department=null, email=123131313, gender=女, id=4, lastName=張三]], id=1]
二、消極式載入
什麼是消極式載入?
當我們在某項業務裡需要同時擷取A、B兩份資料,但是B這份資料又不需要立即使用(或者存在壓根就不會使用的情況),當程式需要載入B時,再去請求資料庫來擷取B資料,而不是一次性將資料全部取出來或者重新發送一份請求,這就是消極式載入。
MyBatis預設關閉消極式載入技術,需要我們在設定檔裏手動配置,配置如下:
<!-- 設定 --> <settings> <!-- 駝峰命名映射 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <setting name="jdbcTypeForNull" value="NULL"/> <!-- 懶載入設定 --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 侵入懶載入,設定為false則按需載入,否則會全部載入 --> <setting name="aggressiveLazyLoading" value="false"/> <!-- 標準日誌輸出 --> <!--<setting name="logImpl" value="STDOUT_LOGGING"/>--> <!-- log4j日誌輸出 --> <setting name="logImpl" value="LOG4J"/> </settings>
注意:
1. lazyLoadingEnabled與aggressiveLazyLoading必須全部設定,且lazyLoadingEnabled為true,aggressiveLazyLoading為false才能讓消極式載入真正生效
2. toString與重載方法過濾:
通常我們在測試時會在實體類加入toString,或者存在了一些重載方法,這些MyBatis會對其進行過濾,但是過濾會調 用cglib與asm指定包,因此要將兩個包添加到buildpath。以下為兩個包的maven依賴:
<dependency> <groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.1</version> </dependency> <dependency> <groupId>asm</groupId> <artifactId>asm</artifactId> <version>3.3.1</version> </dependency>
3.如果想單個開啟或禁用消極式載入,可以使用fetchType屬性來實現
<!-- collection分布查詢 -->
<resultMap type="com.xuyong.entity.Department" id="DepartAndEmpByStep">
<id column="id" property="id"/>
<result column="department_name" property="departmentName"/>
<!-- 多個值傳遞可封裝成map如:column="{key1=column1,...}"
fetchType="lazy" 表示使用懶載入 fetchType="eager"表示禁用懶載入
-->
<collection property="employee" select="com.xuyong.dao.EmployeeDaoPlus.getEmpByDid" column="{id=id}" fetchType="lazy"></collection>
</resultMap>
MyBatis學習——分步查詢與消極式載入