本篇介绍多对多的双向关联,也是Hibernate关系映射的结尾篇,后续篇章会介绍Hibernate的懒加载机制和级联相关操作。
首先还是来模拟一个实际应用的场景,在之前开发过的RBAC模型中,有这样一个关系,用户组中包含多个用户,每一个用户可以属于多个用户组,那么这就是一个多对多的关系,而我又需要从当前用户组读取到包含哪些用户,又需要从当前用户获取他在哪些组中。那么就需要建立双向关联。
Entity:UserGroup
Attr:id(Integer)
name(String)
users(Set< User >)
Entity:User
Attr:id(Integer)
name(String)
userGroups(Set< UserGroup >)
下面是数据库表结构:
可以看到,t_usergroup_user作为中间表,里面有一个字段userGroupId作为外键指向了t_usergroup表,参考了该表的id,另一个外键userId指向了t_user表,参考了该表的id。这样的结构,和单向关联没区别,下面就看实体类的书写。
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@Entity
@Table(name="t_usergroup")
public class UserGroup {
private Integer id;
private String name;
private Set<User> users = new HashSet<User>();
public UserGroup() {
super();
}
public UserGroup(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany
@JoinTable(name="t_usergroup_user",
joinColumns={@JoinColumn(name="userGroupId")},
inverseJoinColumns={@JoinColumn(name="userId")})
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
@JoinTable(name=”t_usergroup_user”中间表的名字
joinColumns={@JoinColumn(name=”userGroupId”)},中间表参考该实体id的那个外键的名称
inverseJoinColumns={@JoinColumn(name=”userId”)})对方实体在中间表中的名称
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
@Entity
@Table(name="t_user")
public class User {
private Integer id;
private String name;
private Set<UserGroup> userGroups= new HashSet<UserGroup>();
public User() {
super();
}
public User(Integer id, String name) {
super();
this.id = id;
this.name = name;
}
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(mappedBy="users")
public Set<UserGroup> getUserGroups() {
return userGroups;
}
public void setUserGroups(Set<UserGroup> userGroups) {
this.userGroups = userGroups;
}
}
@ManyToMany(mappedBy=”users”)既然是双向关联,那么就要设置mappedBy属性,该属性指被UserGroup实体中的users这个属性映射。
实体类书写完毕后,我们来进行CRUD的测试
首先是添加数据的测试,testCreate()
@Test
public void testCreate(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
UserGroup ug1 = new UserGroup();
ug1.setName("超级管理员组");
UserGroup ug2 = new UserGroup();
ug2.setName("普通用户组");
User u1 = new User();
u1.setName("admin");
User u2 = new User();
u2.setName("wan");
Set<User> userSet1 = new HashSet<User>();
userSet1.add(u1);
userSet1.add(u2);
Set<User> userSet2 = new HashSet<User>();
userSet2.add(u1);
//超级管理员组只有一个admin
ug1.setUsers(userSet2);
//普通用户组包含两个用户
ug2.setUsers(userSet1);
session.saveOrUpdate(ug1);
session.saveOrUpdate(ug2);
session.saveOrUpdate(u1);
session.saveOrUpdate(u2);
session.getTransaction().commit();
}
结果:
接下来是读取操作,testRead()
@Test
public void testRead(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
//用户组---->用户
UserGroup ug = (UserGroup) session.load(UserGroup.class, 2);
System.out.println("名称:"+ug.getName());
for(User u : ug.getUsers()){
System.out.println("包含了以下用户:"+u.getName());
}
//用户---->用户组
User u = (User) session.load(User.class, 1);
System.out.println("用户名称:"+u.getName());
for(UserGroup ugg : u.getUserGroups()){
System.out.println("在以下用户组中:"+ugg.getName());
}
session.getTransaction().commit();
}
结果:
Hibernate:
select
usergroup0_.id as id6_0_,
usergroup0_.name as name6_0_
from
t_usergroup usergroup0_
where
usergroup0_.id=?
名称:普通用户组
Hibernate:
select
users0_.userGroupId as userGrou1_1_,
users0_.userId as userId1_,
user1_.id as id7_0_,
user1_.name as name7_0_
from
t_usergroup_user users0_
left outer join
t_user user1_
on users0_.userId=user1_.id
where
users0_.userGroupId=?
包含了以下用户:admin
包含了以下用户:wan
用户名称:admin
Hibernate:
select
usergroups0_.userId as userId1_,
usergroups0_.userGroupId as userGrou1_1_,
usergroup1_.id as id6_0_,
usergroup1_.name as name6_0_
from
t_usergroup_user usergroups0_
left outer join
t_usergroup usergroup1_
on usergroups0_.userGroupId=usergroup1_.id
where
usergroups0_.userId=?
在以下用户组中:超级管理员组
在以下用户组中:普通用户组
可以看到双向关联从哪边都可以找到另一边
接下来是修改操作,testUpdate()
@Test
public void testUpdate(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
UserGroup ug1 = (UserGroup) session.load(UserGroup.class, 1);
ug1.setName("Super User Group");
UserGroup ug2 = (UserGroup) session.load(UserGroup.class, 2);
ug2.setName("Normal User Group");
session.saveOrUpdate(ug1);
session.saveOrUpdate(ug2);
session.getTransaction().commit();
}
结果:
最后是删除操作,testDelete()
@Test
public void testDelete(){
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
UserGroup ug2 = (UserGroup) session.load(UserGroup.class, 2);
ug2.setUsers(null);
session.delete(ug2);
session.getTransaction().commit();
}
结果:
Hibernate:
delete
from
t_usergroup_user
where
userGroupId=?
Hibernate:
delete
from
t_usergroup
where
id=?
这次我们看show sql出来的语句,先删除了中间表中的关系,再删除用户组,这样的操作是对的。
那么Hibernate关系映射系列就介绍到这里,希望对你有所帮助