1. 程式人生 > >Mybatis 學習筆記(六)——延遲載入

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"設定後才啟動延時載入,也可不在此設定,而在配置檔案中設定全域性引數 lazyLoadingEnabledaggressiveLazyLoading,,但你會發現只設置 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

  根據這兩次的日誌輸出情況可以看出,查詢訂單的使用者資訊是延遲載入的(按需載入)。