Hibernate 关系传递
一对多的单向关联
可以通过Classes 到 Student, 但不能从 Student 到 Classes.
1
2
3
4
5
6
7
8
9
10
11
public class Classes {
@BeanProperty private Long cid;
@BeanProperty private String cname;
@BeanProperty private String description;
@BeanProperty private Set<Student> students;
}
public class Student {
@BeanProperty private Long sid;
@BeanProperty private String sname;
@BeanProperty private String description;
}
映射文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<hiberante-mapping >
<class name ="Classes" >
<id name ="cid" length ="5" type ="long" >
<gernerator class ="increment" > </gernerator >
</id >
<property name ="cname" length ="20" type ="string" > </property >
<property name ="description" length ="100" type ="java.lang.String" > </property >
<set name ="students" >
<key >
<column name ="cid" > </column >
</key >
<one-to-many class ="Student" />
</set >
</class >
</hiberante-mapping >
<hibernate-mapping >
<class name ="Student" >
<id name ="sid" length ="5" >
<generator class ="increment" > </generator >
</id >
<property name ="sname" length ="20" > </property >
<property name ="descscription" length ="100" > </property >
</class >
</hibernate-mapping >
执行操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Session session = sessionFactory.openSession ()
Transaction transaction = session.beginTransaction ()
Classes classes = new Classes()
classes.setCname ("第八期" )
classes.setDescription ("很牛" )
session.save (classes)
Student student = new Student()
student.setSname ("班长" )
student.setDescription ("很牛" )
// 如果学生很多, 要执行很多次, 可以用级联(Cascade)来解决
session.save (student)
transaction.commit ()
session.close ()
级联(Cascade)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<set name ="students" cascade ="save-update" inverse ="true" >
</set >
在保存 Classes 时, 同时保存 students
cascade 通过更新班级 级联保存学生(决定插入学生, 但不决定建立外键关联)
inverse 建立班级和学生之间的关系(决定是不是建立外键关联)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Classes classes = new Classes()
classes.setCname ("第八期" )
classes.setDescription ("很牛" )
Student student = new Student()
student.setSname ("班长" )
student.setDescription ("很牛" )
Set students = new HashSet<Student>()
students.add (student)
// 建立 classes 和 student 之间的关联
classes.setStudents (students)
// student 还是临时状态对象, 在保存班级的时候同时保存学生
// 进行了级联操作
session.save (classes)
// 删除一个学生
session.delete (student)
把一个学生从一个班级转到另一个班级
1
2
3
4
5
6
7
8
9
10
11
// 可以不写, 原因如下
// Classes c1 = session.get (Classes.class , 1 L)
Classes c2 = session.get (Classes.class , 2 L)
Student s = session.get (Student.class , 1 L)
// 会执行数据库删除外键操作, 而随后一句会执行更新外键操作, 就变成 2 条 sql 语句
// 所以 第一条可以不写
// c1.getStudents ().remove (s)
c2.getStudents ().add (s)
// 因为都是持久化状态, 所以不用 `save` 或 `update`
transaction.commit ()
session.close ()
删除Classes中的所有学生
1
2
3
4
5
6
7
Classes classes = session.get (Classes.class , 1 L)
// 方式一: 效率不高, 因为要先查询
Set students = classes.getStudents ()
students.clear ()
// 方式二
classes.setStudents (null)
session.close ()
删除一个班级
1
2
3
4
Classes c = session.get (Classes.class , 5 L);
session.delete (c);
1
2
3
4
5
6
7
8
Classes classes = new Classes();
classes.setCname("xxx" );
Student student = new Student();
classes.getStudents().add(student);
session.save(classes);
把 session.save()/update()
一个对象的操作是显示操作,
级联一个对象是一个隐式操作
一对多的双向
1
2
3
4
5
6
<class name ="student" >
<many-to-one name ="classes" class ="io.zhpooer.Classes" column ="cid" />
</class >
一对多涉及到关系操作, 在多的一方操作效率高, 因为多的一方不会执行update
外键操作
1
2
3
4
5
Student student = new Student()
Classes classes = session.get (Classes.class , 3 L)
classes.getStudents .add (student)
student.setClasses (classes)
session.save (student)
删除关系的错误
1
2
3
4
5
6
7
Classes c = session.get (Classes.class , 1 L);
Set students = classes.getStudents();
for (Student s:students) {
session.delete (student);
}
一对多总结
如果让一的一方维护关系,取决于的因素有
在一的一方的映射文件中,set元素的inverse属性为default/false
在客户端的代码中,通过一的一方建立关系
session.save/update
方法是用来操作表的,和操作关系没有关系
怎么样采用级联的方法通过保存一个对象从而保存关联对象
如果session.save
操作的对象是A,这个时候应该看A.hbm.xml,看set元素中cascade是否设置有级联保存
在客户端通过A建立关联
在客户端执行session.save(A)
一对多的情况,多的一方维护关系效率比较高
在多的一方many-to-one中没有inverse属性
在客户端通过多的一方建立关联
多对多
学生和课程的关系是多对多, 多对多不用配置 cascade
.
一方可以设置 inverse=true
, 放弃外键的维护权,
如果两方都维护, 可能会发生冲突
1
2
3
4
5
6
7
8
9
10
public class Student {
@BeanProperty private Long sid;
@BeanProperty private String name;
@BeanProperty private Set<Course> courses;
}
public class Course {
@BeanProperty private Set<Student> students;
@BeanProperty private Long cid;
@BeanProperty private name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<class name ="Student" >
<id name ="sid" >
<generator class ="increment" > </generator >
</id >
<set name ="courses" table ="student_course" >
<key >
<column name ="sid" > </column >
</key >
<many-to-many class ="Cource" column ="cid" > </many-to-many >
</set >
</class >
<class name ="Course" >
<id name ="cid" >
<generator class ="increment" > </generator >
</id >
<set name ="students" table ="student_course" >
<key >
<column name ="cid" > </column >
</key >
<many-to-many class ="Student" column ="sid" > </many-to-many >
</set >
</class >
关系操作:
多对多, 谁操作, 效率都一样
解除关系, 把第三张表一行数据删除掉
建立关系, 把第三张表数据增加一行记录
变更关系, 对第三张表 执行update操作, 先删除后增加
级联操作: 都是对象针对集合的操作
一对一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<many-to-one unique ="true" > </many-to-one >
<one-to-one name ="company" property-ref ="address" class ="Address" > </one-to-one >
<class name ="Person" >
<id name ="pid" >
<generator class ="foreign" >
<param name ="property" > address </param >
</generator >
</id >
<one-to-one name ="address" constrained ="true" class ="Address" > </one-to-one >
</class >
Hibernate 注解应用
简化 hbm 配置
版本3.6, 导入 hibernate-annotation-jpa.jar
使用注解配置 PO 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/难难难/ 优先导入
import javax.persistance.*;
@Entity
@Tabel (name="" )
public class Book {
@Id
@GeneratedValue (strategy=GenerationType.IDENTITY)
private Integer id;
@Column (name="" , length=, unique)
private String name;
@Temporal (TemporalType.TIME | TemporalType.DATE | TemporalType.TIMESTAMP)
private Date time;
@Transient
private String noImportant;
}
1
2
<mapping class ="Book" > </mapping >
使用Hibernate策略 uuid
1
2
3
4
5
6
7
8
9
10
@Entity
@Table (name="person" )
public class Person {
@Id
@GenericGenerator (name="myuuidGen" , strategy="uuid" )
@GeneratedValue (generator="myuuidGen" )
private String id;
private String name;
}
多表注解配置
一对多
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Entity
@Table (naem="customer" )
public class Customer {
@Id
@GenerateValue (strategy=GenerationType.AUTO)
private Integer id;
private String name;
@OneToMany (targetEntity=Order.class, mappedBy="customer" )
@Cascade (value={CascadeType.SAVEUPDATE})
private Set<Order> orders;
}
public class Order {
@Id
@GenerateValue (strategy=GenerationType.AUTO)
private Integer id;
private String addr;
@ManyToOne (targetEntity=Customer.class)
@JoinColomn (name="customer_id" )
private Customer customer;
}
多对多
配置多对多时, 只需要一端配置中间表, 另一端要配置 mappedBy(放弃外键)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@NamedQueries (value=(@NamdQuery (name="findByName" , query="from Customer" ))
public class Student {
private Integer id;
private String name;
@ManyToMany (targetEntity=Course.class)
@JoinTable (name="student_couse" , joinColumn={@JoinColumn (name="" )},
inverseJoinColumns={@JoinColumn (name="" )})
@Fetch (FetchMode.Select)
@LazyCollection (LazyToCollectionOption.FALSE)
private Set courses;
@LazytoOn (LazyToOneOption.FALSE)
private Classes class ;
}
public class Course {
@ManyToMany (targetEntity=Student.class, mappedBy="courses" )
private Set students;
}
继承类型映射
三种继承策略
父类和子类的数据同一张表保存, 引入辨别者, 区分数据是父类数据还是子类数据
join-subclass 父类和子类数据都是单独一张表,表之间通过外键表示继承关系
unions-subclass(了解 ) 父类和子类都是单独一张表, 表之间没有任何联系
subclass子类映射
1
2
3
public class Employee { }
public class HourEmployee extends Employee { }
public class SalaryEmployee extends Employee { }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<hibernate-mapping >
<class name ="cn.itcast.subclass.Employee" table ="employee"
catalog ="hibernate3day4" discriminator-value ="ee" >
<id name ="id" >
<generator class ="identity" > </generator >
</id >
<discriminator column ="etype" > </discriminator >
<property name ="name" > </property >
<subclass name ="cn.itcast.subclass.HourEmployee" discriminator-value ="he" >
<property name ="rate" > </property >
</subclass >
<subclass name ="cn.itcast.subclass.SalaryEmployee" discriminator-value ="se" >
<property name ="salary" > </property >
</subclass >
</class >
</hibernate-mapping >
join-subclass子类映射
为父类数据和子类数据分布建表,公共信息放入父类表,
个性信息放入子类表,通过外键关联
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<hibernate-mapping >
<class name ="cn.itcast.joinedsubclass.Employee" table ="employee" catalog ="hibernate3day4" >
<id name ="id" >
<generator class ="identity" > </generator >
</id >
<property name ="name" > </property >
<joined-subclass name ="cn.itcast.joinedsubclass.HourEmployee" table ="h_employee" >
<key column ="eid" > </key >
<property name ="rate" > </property >
</joined-subclass >
<joined-subclass name ="cn.itcast.joinedsubclass.SalaryEmployee" table ="s_employee" >
<key column ="eid" > </key >
<property name ="salary" > </property >
</joined-subclass >
</class >
</hibernate-mapping >
结论
优先使用 joined-subclass, 如果类信息非常少, 也可以使用 subclass
集合映射
在实际开发中都是用有序的集合
在hbm中使用 bag
list
set
set
不允许重复, 无序
list
允许重复, 有序
bag
无序,可以重复, 性能最好
1
2
3
4
5
6
7
8
9
10
11
12
<bag name ="article" >
<key column ="auther_id" > </key >
<one-to-many class ="Article" > </one-to-many >
</bag >
<list name ="article" cascade ="all" >
<key column ="author_id" > </key >
<list-index column ="article_index" > </list-index >
<one-to-many class ="Article" > </one-to-many >
</list >