Hibernate中集合的延迟加载(lazy)和抓取策略(fetch)

Java 发表评论

在开发博客项目的时候实体类之间有一对多和多对一的关系,看了一下API文档和网上的分享,谈一下一对多关系中,set集合的lazy属性和fetch属性不同搭配的理解。

不同配置的结果如下:

lazy_fetch

下面用测试说明具体情况。

User和category是一对多关系,实体类代码如下。

public class User {
	private Long user_id;
	private String username;
	private String password;
	private String status;//状态
	private String details;//备注
	
	private Set<Category> categories = new HashSet<Category>();
	
	public Set<Category> getCategories() {
		return categories;
	}
	public void setCategories(Set<Category> categories) {
		this.categories = categories;
	}
	public Long getUser_id() {
		return user_id;
	}
	public void setUser_id(Long user_id) {
		this.user_id = user_id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getStatus() {
		return status;
	}
	public void setStatus(String status) {
		this.status = status;
	}
	public String getDetails() {
		return details;
	}
	public void setDetails(String details) {
		this.details = details;
	}
	
}
public class Category {
	private Long cate_id;
	private String title;
	private User user;
	
	public Long getCate_id() {
		return cate_id;
	}
	public void setCate_id(Long cate_id) {
		this.cate_id = cate_id;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public User getUser() {
		return user;
	}
	public void setUser(User user) {
		this.user = user;
	}
}

对应的映射文件配置,下面测试仅改变User类的映射文件中Set标签的lazy和fetch属性:

<hibernate-mapping package="com.dengtuzi.domain">
    	<class name="User" table="dtb_user" >
    		<id name="user_id" >
    			<generator class="native"></generator>
    		</id>
    		<property name="username"></property>
    		<property name="password"></property>
    		<property name="status"></property>
    		<property name="details"></property>
    		
    		<set name="categories" lazy="true" fetch="select">
    			<key column="cate_user_id"></key>
    			<one-to-many class="Category" />
    		</set>
    	</class>
    </hibernate-mapping>
 <hibernate-mapping package="com.dengtuzi.domain">
    	<!--  -->
    	<class name="Category" table="dtb_category">
    		<id name="cate_id">
    			<generator class="native"></generator>
    		</id>
    		<property name="title" ></property>
    		
    		<many-to-one  name="user" column="cate_user_id" class="User"></many-to-one>
    	</class>
    </hibernate-mapping>

1. lazy=”true”  fetch=”select”

测试代码:

   @Test
	public void fun1(){
		//加载配置
		Configuration config = new Configuration().configure();
		//获得factory
		SessionFactory factory = config.buildSessionFactory();
		//获得session
		Session session = factory.openSession();
		//开启事务
		Transaction transaction = session.beginTransaction();
		
		User user = session.get(User.class, 14l);//断点位置
		//查询用户关联的set集合
		Set<Category> categories = user.getCategories();
		
		//使用数据
		System.out.println(categories);
		
		//提交事务,关闭session
		transaction.commit();
		session.close();

下断点,开始调试。

往下走一步,执行完user查询,控制台输出的sql语句:

lazy_01
再往下走一步,执行该用户关联的categories的查询,控制没有输出。

继续往下走,执行打印categories代码,此时控制台输出结果:

lazy_02

从测试的结果可以看到,查询user时并没有进行关联集合的查询,获取categories的值也没有进行查询,使用categories数据时才进行查询操作,即开启了延迟加载并且user和categories的查询都用的是单表查询语句。

2.lazy=”false”  fetch=”select”
在User的映射文件更改Set集合属性配置,使用同样的测试代码。
执行user的查询语句后,控制台信息:
lazy_03
对user查询的同时,立即对关联的categories集合也进行了查询,也就是关闭了关联集合的延迟加载,而且user和categories的查询都使用了单表查询语句
3.lazy=”extra”  fetch=”select”
测试结果和情况1相同,看不出区别,需要修改测试代码。看了官方API文档,里面描述是

使用"extra",此时大多数操作不会初始化集合类(适用于非常大的集合) 

修改测试代码在打印categories前加入一条打印语句:

	@Test
	public void fun1(){
		//加载配置
		Configuration config = new Configuration().configure();
		//获得factory
		SessionFactory factory = config.buildSessionFactory();
		//获得session
		Session session = factory.openSession();
		//开启事务
		Transaction transaction = session.beginTransaction();
		
		User user = session.get(User.class, 14l);
		//查询用户关联的set集合
		Set<Category> categories = user.getCategories();
		
		//使用数据
		System.out.println(categories.isEmpty());
		System.out.println(categories);
		
		//提交事务,关闭session
		transaction.commit();
		session.close();
	}

执行user查询后控制台打印的结果:

lazy_04
继续往下执行,执行categories查询时没有加载数据,控制台没有打印信息。
执行打印categories集合是否为空的语句,控制台打印信息:
lazy_05
、继续往下执行,打印categories,此时控制台输出信息:
lazy_06
从测试结果可以看到,效果和lazy=“true”效果差不多,查询时并不加载数据,使用时才查询加载。唯一区别是,lazy=”extra”时,如果使用集合时,如果不涉及集合内容(判断是否空集,查询集合size),也是不会加载集合数据。
4.lazy=”true”  fetch=”join”
调试结果:
lazy_07
查询user的时候使用多表查询语句,同时把关联的categories集合也进行了查询,也就是关闭了延迟加载,lazy属性被无效化了。
5.lazy=”false”   fetch=”join”
测试结果同情况4一致,使用多表查询,关闭延迟加载。
6.lazy=”extra” fetch=”join”
测试结果同情况4一致,使用多表查询,关闭延迟加载。

7.lazy=”true”  fetch=”subselect”
测试代码:
	@Test
	public void fun2(){
		//加载配置
		Configuration config = new Configuration().configure();
		//获得factory
		SessionFactory factory = config.buildSessionFactory();
		//获得session
		Session session = factory.openSession();
		//开启事务
		Transaction transaction = session.beginTransaction();
		
		//查询所有用户
		String hql = "FROM User";
		Query query = session.createQuery(hql);//断点位置
		List<User> users = (List<User>)query.list();
		
		System.out.println(users.get(0).getCategories().size()); 
		System.out.println(users.get(0).getCategories());
		//提交事务,关闭session
		transaction.commit();
		session.close();
	}

查询所有user时,控制台信息:

lazy_08
打印categories集合size时候控制台信息:
lazy_09
继续执行,打印categories集合:
lazy_10
从测试结果来看,查询user时并不查询关联的categories集合,而是在使用categories时通过子查询语句查询,即开启了延迟加载
8.lazy=”false”  select=”subselect”
查询所有user时,控制台信息:
lazy_11
查询user时就立即使用子查询语句查询关联的categories集合,即关闭了延迟加载
9.lazy=”extra”  select=”subselect”
查询所有user时控制台信息:
lazy_12
打印categories集合size:
lazy_13
打印categories集合:
lazy_14
查询所有user的时候不查询关联的categories集合,不使用集合内数据时(判断是否为空,查询集合size)也不查询,只有使用到集合内元素数据时才使用子查询语句进行查询,即开启延迟加载
10.结论
在不影响代码功能的多数情况下,效率最好的lazy=”true”,fetch=”select”,而这也是Hibernate默认配置。

发表评论

邮箱地址不会被公开。 必填项已用*标注

昵称 *