fetch为LAZY、EAGER在load时的区别

有两个类,Group,User,设置它们之间为多对一的双向关联关系

分别如下:

Group类

package com.chen.hibernate.ormapping;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "t_group")
public class Group {
	private int id;
	private String name;
	private Set<User> users = new HashSet<User>();

	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	// 这里的mappedBy为User类中的getGroup决定,设置fetch为eager,一的一方默认为lazy
	@OneToMany(mappedBy = "group", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
	public Set<User> getUsers() {
		return users;
	}

	public void setUsers(Set<User> users) {
		this.users = users;
	}
}


User类

package com.chen.hibernate.ormapping;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "t_user")
public class User {
	private int id;
	private String name;
	private Group group;

	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	//设置fetch为lazy,多的一方默认问eager
	@ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
	@JoinColumn(name = "groupId")
	public Group getGroup() {
		return group;
	}

	public void setGroup(Group group) {
		this.group = group;
	}
}

junit测试类

package com.chen.hibernate.ormapping;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

public class CRUDTest {
	private static SessionFactory sessionFactory = null;

	@BeforeClass
	public static void beforeClass() {
		new SchemaExport(new AnnotationConfiguration().configure()).create(
				false, true);
		sessionFactory = new AnnotationConfiguration().configure()
				.buildSessionFactory();
	}

	@Test
	public void testSaveGroup() {
		User user = new User();
		user.setName("u1");

		User user2 = new User();
		user2.setName("u2");
		Group group = new Group();
		group.setName("g1");
		group.getUsers().add(user);
		group.getUsers().add(user2);

		//注意,虽然两边都是级联的方式,但這裡的user也要把group设一下,不然,save(group)的时候,user表中的
		//groupId字段仍是null
		user.setGroup(group);
		user2.setGroup(group);

		Session session = sessionFactory.getCurrentSession();
		session.beginTransaction();
		session.save(group);
		session.getTransaction().commit();
	}

	@Test
	public void testLoadUser1() {
		//先seve一下,让数据库里有记录,然后测试load
		testSaveGroup();

		Session session = sessionFactory.getCurrentSession();
		session.beginTransaction();
		//这条语句不会发出sql语句,只是产生一个user代理
		User user = (User) session.load(User.class, 2);
		//这句会发出两段sql语句,如下
		System.out.println(user.getGroup().getName());
		session.getTransaction().commit();

		System.out.println(user.getName());

	}

	@AfterClass
	public static void afterClass() {
		sessionFactory.close();
	}

}

执行完testSaveGroup()后,数据库的表情况为

t_group表


t_user表


log4j的日志记录如下:

21:51:51,250  INFO SchemaExport:226 - Running hbm2ddl schema export
21:51:51,255 DEBUG SchemaExport:242 - import file not found: /import.sql
21:51:51,256  INFO SchemaExport:251 - exporting generated schema to database
21:51:51,369 DEBUG SchemaExport:377 - 
    alter table t_user 
        drop constraint FKCB63CCB625144E67
21:51:51,384 DEBUG SchemaExport:377 - 
    drop table t_group
21:51:51,388 DEBUG SchemaExport:377 - 
    drop table t_user
21:51:51,390 DEBUG SchemaExport:377 - 
    drop sequence hibernate_sequence
21:51:51,393 DEBUG SchemaExport:377 - 
    create table t_group (
        id int4 not null,
        name varchar(255),
        primary key (id)
    )
21:51:51,402 DEBUG SchemaExport:377 - 
    create table t_user (
        id int4 not null,
        name varchar(255),
        groupId int4,
        primary key (id)
    )
21:51:51,407 DEBUG SchemaExport:377 - 
    alter table t_user 
        add constraint FKCB63CCB625144E67 
        foreign key (groupId) 
        references t_group
21:51:51,438 DEBUG SchemaExport:377 - 
    create sequence hibernate_sequence
21:51:51,440  INFO SchemaExport:268 - schema export complete
Hibernate: 
    select
        nextval ('hibernate_sequence')
Hibernate: 
    select
        nextval ('hibernate_sequence')
Hibernate: 
    select
        nextval ('hibernate_sequence')
Hibernate: 
    insert 
    into
        t_group
        (name, id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        t_user
        (groupId, name, id) 
    values
        (?, ?, ?)
Hibernate: 
    insert 
    into
        t_user
        (groupId, name, id) 
    values
        (?, ?, ?)
Hibernate: 
    select
        user0_.id as id3_0_,
        user0_.groupId as groupId3_0_,
        user0_.name as name3_0_ 
    from
        t_user user0_ 
    where
        user0_.id=?
Hibernate: 
    select
        group0_.id as id2_1_,
        group0_.name as name2_1_,
        users1_.groupId as groupId3_,
        users1_.id as id3_,
        users1_.id as id3_0_,
        users1_.groupId as groupId3_0_,
        users1_.name as name3_0_ 
    from
        t_group group0_ 
    left outer join
        t_user users1_ 
            on group0_.id=users1_.groupId 
    where
        group0_.id=?
g1
u1


关注最后两个select,这两个select是由testLoadUser1中的System.out.println(user.getGroup().getName());这句产生的,因为load的是个user,所以先从user表取,但是user的fetch设置了lazy,所以只是查询了user表,然后要取user表对应group的名字,这时候就要到group表中取了,因为group的fetch为eager,所以,会把跟group表有关联的user表的两条记录也取出来。

这里接着实验,若把group和user的fetch都设为lazy,可以设想结果是先单独select  user表,然后单独select group表

Hibernate: 
    select
        user0_.id as id3_0_,
        user0_.groupId as groupId3_0_,
        user0_.name as name3_0_ 
    from
        t_user user0_ 
    where
        user0_.id=?
Hibernate: 
    select
        group0_.id as id2_0_,
        group0_.name as name2_0_ 
    from
        t_group group0_ 
    where
        group0_.id=?
g1
u1
实验结果的确是这样。

若user的fetch为eager,group的fetch为lazy(都为默认值)。结果如下:

Hibernate: 
    select
        user0_.id as id3_1_,
        user0_.groupId as groupId3_1_,
        user0_.name as name3_1_,
        group1_.id as id2_0_,
        group1_.name as name2_0_ 
    from
        t_user user0_ 
    left outer join
        t_group group1_ 
            on user0_.groupId=group1_.id 
    where
        user0_.id=?
g1
u1

user的eager在load的效果是对group做左连接,并不是所有情况下eager都是这种效果,如在HQL中就是发出两条sql语句,这段可忽略,接***。

	@Test
	public void testHQL_10() {
		Session session = sf.openSession();
		session.beginTransaction();
		Query q = session.createQuery("from Topic t where t.category.id = 1");
		List<Topic> topics = (List<Topic>) q.list();
		for (Topic t : topics) {
			System.out.println(t.getTitle());
		}
		session.getTransaction().commit();
		session.close();

	}

运行的日志为:

Hibernate: 
    select
        topic0_.id as id2_,
        topic0_.category_id as category4_2_,
        topic0_.createDate as createDate2_,
        topic0_.title as title2_ 
    from
        Topic topic0_ 
    where
        topic0_.category_id=1
Hibernate: 
    select
        category0_.id as id0_0_,
        category0_.name as name0_0_ 
    from
        Category category0_ 
    where
        category0_.id=?
t0
t1
t2
t3
t4
t5
t6
t7
t8
t9

***这样就只产生了一条select,因为user的eager第一次就连接group表把所以信息都查出来了,而此时group的fetch为lazy,取group的时候不会产生“连锁”反应再取user。什么是这种“连锁”反应,看最后一种情况,若设置两边都为eager,这时候又发生两次查询,因为user的fetch为eager,查user表时会连接group表把group的信息也取出来,但是group的fetch也为eager,在取group的时候,就会产生“连锁”反应,会再发一次select,取user表一次,注意“连锁”反应不会一直持续,所以就产生两次select。

Hibernate: 
    select
        user0_.id as id3_1_,
        user0_.groupId as groupId3_1_,
        user0_.name as name3_1_,
        group1_.id as id2_0_,
        group1_.name as name2_0_ 
    from
        t_user user0_ 
    left outer join
        t_group group1_ 
            on user0_.groupId=group1_.id 
    where
        user0_.id=?
Hibernate: 
    select
        users0_.groupId as groupId1_,
        users0_.id as id1_,
        users0_.id as id3_0_,
        users0_.groupId as groupId3_0_,
        users0_.name as name3_0_ 
    from
        t_user users0_ 
    where
        users0_.groupId=?
g1
u1

结论:从上可以看出,除了默认值(一的一方为eager,多的一方为lazy)以外的所有情况,都发出多次查询,若考虑效率,就fetch在这种情况下就取默认值就行了。

注意:load方式虽然产生的代理对象,但是若第一次select是两张表连接后产生的,当session关闭后,还可以拿到user里面的值,也可以通过user.getGroup().getName()拿到对应group的名字,不会出现异常的。



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值