MyBatis學習——分步查詢與消極式載入

來源:互聯網
上載者:User

標籤:聲明   簡單   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學習——分步查詢與消極式載入

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.