有两个类,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
这里接着实验,若把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的名字,不会出现异常的。