Hibernate注解的关联关系映射,分为2种,即单向关联映射和双向关联映射,它们最大的区别就是在查询数据时,单向关联只能通过一边进行查询,而双向关联两边都可以进行查询。
单向关联是指只在一方加上注解,
双向关联是指双方都加上注解。这里主要记录双向关联。
1,一对多多对一单向关联
–@ManyToOne:在多的一方维护记录(在tuser2表维护记录)
(1)表结构:tuser2表的groupid字段关联tgroup表的主键id,tuser2是多的一方,tgroup是一的一方:
(2)bean:
@Entity
@Table(name="tuser2")
public class TUser2 implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
@ManyToOne //代表是一对多关联
@JoinColumn(name="groupid") //关联的字段是groupid
private Group group;
...get,set...
}
//Group不需要写任何东西
@Entity
@Table(name="tgroup")
public class Group implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
...get,set...
}
(3)测试
–保存记录
Group group=new Group();
group.setName("manager");
TUser2 user=new TUser2();
user.setName("hyman");
user.setGroup(group);
session.save(group);
session.save(user);
–获取记录(此种写法只能从user一方获取group一方的数据)
TUser2 user=(TUser2)session.get(TUser2.class,1);
System.out.println(user.getName());
System.out.println(user.getGroup().getName());
–更新记录
Group group=new Group();
group.setName("name2");
TUser2 user=(TUser2)session.get(TUser2.class, 1);
user.setGroup(group);
session.save(group);
session.save(user);
–删除记录
Group group=(Group)session.get(Group.class, 5);
session.delete(group);
//如果tuser表中存在groupid为5的记录,后台会报错,因为tuser表中引用了groupid,因此不能删除group
TUser2 user=(TUser2)session.get(TUser2.class, 1);
session.delete(user);
–如果在@ManyToOne后面设置了cascade=CascadeType.ALL,那么就有两种情况:
(1)tuser2不存在相同的groupid的其他记录,此时会删除tuser2表中id为1的记录,也会级联删除group表中相应的记录;
(2)tuser2表存在相同的groupid的其他记录,后台会报错,无法删除。
如果没有设置cascade=CascadeType.ALL,则会删除tuser2表的记录,不会删除group表的记录。
–@OneToMany:在一的一方维护记录(在group表维护记录)
(1)表结构和上面的一样;
(2)bean:
@Entity
@Table(name="tgroup")
public class Group implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinColumn(name="userid") //意味着在user表会多出一列userid,如果不设置这个属性,那么会多出一个中间表,表中两个字段分别是group和user表的id
private Set<TUser2> users;
...get,set...
}
@Entity
@Table(name="tuser2")
public class TUser2 implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
...get,set...
}
(3)测试:
–保存记录
TUser2 user21=new TUser2();
user21.setName("name1");
TUser2 user22=new TUser2();
user22.setName("name2");
Set<TUser2> set=new HashSet<>();
set.add(user21);
set.add(user22);
Group group=new Group();
group.setName("groupname");
group.setUsers(set);
session.save(group);
–获取记录
Group group=(Group)session.get(Group.class, 9);
Set<TUser2> users=group.getUsers();
Iterator us=users.iterator();
while(us.hasNext()){
TUser2 user=(TUser2)us.next();
System.out.println(user.getName());
}
//只能从group一方获取user一方的数据,而不能从user一方获取group一方的数据
–更新记录
Group group=(Group)session.get(Group.class, 9);
group.setName("updatedName");
session.save(group);
–删除记录
Group group=(Group)session.get(Group.class, 9);
session.delete(group);
–如果在@OneToMany后面设置了cascade=CascadeType.ALL,那么会级联删除user表中的记录,如果没有设置,那么会清空user表的groupid字段,然后删除group表记录。
2,一对多多对一双向关联
上面的一对多或多对一的关联都有一个不足之处,就是只能从一方获取另一方的数据,双向关联下,就可以从任何一方获取另一方的数据了
(1)表结构:
(2)bean:在一的一方需要设置@OneToMany和mappedby,在多的一方需要设置@ManyToOne和
@JoinColumn:
@Entity
@Table(name="tgroup")
public class Group implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
@OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY,mappedBy="group")
private Set<TUser2> users;
...set,get...
}
@Entity
@Table(name="tuser2")
public class TUser2 implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
@ManyToOne(fetch=FetchType.LAZY,cascade=CascadeType.ALL)
@JoinColumn(name="groupid") //会在user表中多出一列:groupid,如果不设置这个属性,数据库会新增一个中间表维护user和group的记录
private Group group;
...set,get...
}
(3)测试:
–保存记录
TUser2 user21=new TUser2();
user21.setName("name1");
TUser2 user22=new TUser2();
user22.setName("name2");
Set<TUser2> set=new HashSet<>();
set.add(user21);
set.add(user22);
Group group=new Group();
group.setName("groupname2");
group.setUsers(set);
user21.setGroup(group);//必须,否则user表groupid字段为空
user22.setGroup(group);//必须,否则user表groupid字段为空
session.save(group);
–获取记录
(1)从一的一方获取多的一方的记录
Group group=(Group)session.get(Group.class, 9);
Set<TUser2> users=group.getUsers();
Iterator us=users.iterator();
while(us.hasNext()){
TUser2 user=(TUser2)us.next();
System.out.println(user.getName());
}
(2)从多的一方获取一的一方的记录:
TUser2 user=(TUser2)session.get(TUser2.class, 18);
System.out.println(user.getGroup().getName());
–更新记录
Group group=new Group();
group.setName("newGroup");
TUser2 user=(TUser2)session.get(TUser2.class, 18);
user.setGroup(group);
session.save(user);//会自动保存新增的group表的记录
–删除记录
Group group=(Group)session.get(Group.class, 13); session.delete(group);//会级联删除user表中的记录
TUser2 user=(TUser2)session.get(TUser2.class, 3); session.delete(user);//会级联删除group中的相关的记录和user表中的相同groupid的记录
3,多对多单向关联
(1)表:
(2)bean:只在一方维护关系就好,例如在class表一方维护关系,teacher表一方不做处理
@Entity
@Table(name="class")
public class Clazz implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
@ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinTable(
name="class_teacher",//中间表名
joinColumns={@JoinColumn(name="classid")},//当前对象id在中间表的列名
inverseJoinColumns={@JoinColumn(name="teacherid")}//关联的另一个表在中间表的列名
)
private Set<Teacher> teachers;
...set,get...
}
@Entity
@Table(name="teacher")
public class Teacher implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
...set,get...
}
(3)测试:
–保存记录
Clazz clazz1=new Clazz();
Clazz clazz2=new Clazz();
clazz1.setName("class1");
clazz2.setName("class2");
Teacher teacher1=new Teacher();
Teacher teacher2=new Teacher();
Teacher teacher3=new Teacher();
teacher1.setName("teacher1");
teacher2.setName("teacher2");
teacher3.setName("teacher3");
Set<Teacher> teacher1s=new HashSet<>();
Set<Teacher> teacher2s=new HashSet<>();
teacher1s.add(teacher1);
teacher1s.add(teacher2);
teacher2s.add(teacher2);
teacher2s.add(teacher3);
clazz1.setTeachers(teacher1s);
clazz2.setTeachers(teacher2s);
session.save(clazz1);//会级联插入class表,teacher表,中间表
session.save(clazz2);//会级联插入class表,teacher表,中间表
–获取记录:由于只在class的乙方做了维护,因此只能从class表记录获取teacher表记录
Clazz clazz=(Clazz)session.get(Clazz.class, 1);
Set<Teacher> teachers=clazz.getTeachers();
Iterator<Teacher> iterator=teachers.iterator();
while(iterator.hasNext()){
Teacher teacher=iterator.next();
System.out.println(teacher.getName());
}
–更新记录,更新id为1的class的teachers信息
Clazz clazz=(Clazz)session.get(Clazz.class, 1);
Teacher teacher=(Teacher)session.get(Teacher.class, 1);
Set<Teacher> teachers=new HashSet<>();
teachers.add(teacher);
clazz.setTeachers(teachers);
session.save(clazz);//只更新中间表的id对应关系,插入新记录,删除失效的记录
–删除记录
Clazz clazz=(Clazz)session.get(Clazz.class, 2);
session.delete(clazz);//删除class表记录,删除中间表记录,删除teacher表记录
//注意:如果teacher表中不存在和class对应的其他记录,那么teacher表的相关记录会被删除,如果teacher表中存在和class对应的其他记录,后台会报错,无法删除。
4,多对多双向关联
上面的多对多但相关联有一个不足就是只能从class来维护teacher表的记录,双向关联则可以从任何一方维护另一方的记录:
(1)表结构不变;
(2)bean:class不变,teacher只要设置mappedby属性即可:
@Entity
@Table(name="teacher")
public class Teacher implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@Column(name="name")
private String name;
@ManyToMany(mappedBy="teachers",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
private Set<Clazz> clazzs;
...set,get...
}
(3)测试:
–保存记录
双向关联并不是真的互相维护,还是存在控制权的问题,这里就把控制权交给了class。因此,写法和单向关联是一样的。
–获取记录
在双向关联中,既可以通过class来获取teacher的记录,也可以通过teacher获取class的记录:
Clazz clazz=(Clazz)session.get(Clazz.class, 1);
Set<Teacher> teachers=clazz.getTeachers();
Iterator<Teacher> iterator=teachers.iterator();
while(iterator.hasNext()){
Teacher teacher=iterator.next();
System.out.println(teacher.getName());
}
Teacher teacher=(Teacher)session.get(Teacher.class, 5);
Set<Clazz> clazzs=teacher.getClazzs();
Iterator<Clazz> iterator2=clazzs.iterator();
while(iterator2.hasNext()){
Clazz clazz2=iterator2.next();
System.out.println(clazz2.getName());
}
–更新记录
双向关联并不是真的互相维护,还是存在控制权的问题,这里就把控制权交给了class。因此,写法和单向关联是一样的。
–删除记录
Teacher teacher=(Teacher)session.get(Teacher.class, 5);
session.delete(teacher);
首先会删除中间表teacherid=17的记录;然后删除teacher表id=17的记录,删除class表相关的记录(不存在与其他teacherid关联的记录),然后删除中间表中classid=6和7的记录,删除class表中id为6和7的记录,删除teacher表中和classid 6,7关联的记录。所以,删除之后的结果是:
因此,在多对多关联的时候,一定要想清楚自己的目的,不要盲目使用cascadeType.all。否则会删除一些本来不想删除的记录。