Hibernate实体映射——单边的一对多关系
2018-01-19 20:51
281 查看
数据库中的表是有关系的,两个表之间可以通过外键关联。如果把表抽象为实体类,这些关系可以抽象为实体之间的一对一,一对多,多对一等关系。hibernate能处理这些复杂的关系,前提是要正确配置这些关系。
单边的一对多关系
单边的一对多关系是指一方有集合属性,包含多个多方,而多方没有一方的引用。比较典型的是用户与电子邮件。其中,用户为一方,电子邮件为多方,一个用户可以拥有多个电子邮件记录。
Person.java中有id、name、List<Email>;
Email.java中有id、email;
一个person有多个email,而email中没有person;
对应的表中
tb_person:id、name;
tb_email:id、email、person_id;
若person中有email外键,则一个person的email外键要有多个id,不现实;而email中有person外键,一个email只需要有一个person的外键即可
总结:
一个person有多个email,则person类中有多个email,而person表中没有email外键,email表中有person外键;
具体实现如下:
Person.java
[html] view
plain copy
package com.lmb.hibernate.bean;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
@Entity
@Table(name="tb_person")
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
//一对多配置,并配置列关系
<span style="color:#ff0000;">@OneToMany(fetch=FetchType.EAGER,
targetEntity=Email.class,
cascade={CascadeType.PERSIST,CascadeType.REMOVE,CascadeType.MERGE,CascadeType.REFRESH}
)
@JoinColumns(value = { @JoinColumn(name="person id",referencedColumnName="id") })
//配置排序方法
@OrderBy(value="email desc")
</span> private List<Email> emails=new ArrayList<Email>();
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;
}
public List<Email> getEmails() {
return emails;
}
public void setEmails(List<Email> emails) {
this.emails = emails;
}
}
Person实体中使用@OneToMany配置一对多关系。fetch配置加载方式(延迟加载fetch=FetchType.LAZY、即时加载fetch=FetchType.EAGER);targetEntity配置集合属性中的类型,由于emails属性类型为List<Email>,用反省制定了集合内为Email对象,因此targetEntity可省略;cascade配置级联方式,本例配置为PERSIST、REMOVE、MERGE、REFRESH,表示在保存、删除、修改、刷新Person类时会自动在数据库中保存、删除、修改、刷新属于它的Email对象。配置级联关系后,可以通过操作Person类来操作Email类。该配置也等同于CascadeType.ALL。@OrderBy配置排序方式,本例将Email按照email列降序排列。
Email实体类比较简单,由于是单边关系映射,Email中没有用户实体的引用,只是一个简单的POJO。
Email.java
[html] view
plain copy
package com.lmb.hibernate.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="tb_email")
public class Email {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
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;
}
}
配置完毕后要将Person类和Email类在hibernate配置文件中声明为实体类。实体类要配置在<session-factory />中。
hibernate.cfg.xml
[html] view
plain copy
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate?characterEncoding=UTF-8</property>
<property name="connection.username">lmb</property>
<property name="connection.password">lmb</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<span style="color:#ff0000;"><mapping class="com.lmb.hibernate.bean.Person" />
<mapping class="com.lmb.hibernate.bean.Email" />
</span>
</session-factory>
</hibernate-configuration>
以上使用@注解实现的,当然我们还可以使用XML配置的方式实现,这了我就不在列出。
延迟加载与即时加载
即时加载(Eagar Fetching),查询Person的时候自动加载相关的Email
延迟加载(Lazy Fetching),只有在需要的时候才查询Email(默认)
延迟加载异常
一般来说,延时加载比即时加载节省资源。但是如果处理不当,延迟加载容易抛出延时加载异常(LazyInitializationException)。这是因为延迟加载时,只有第一次调用person.getEmail()才会加载emails数据,如果这时候数据库连接已经关闭了,就会因为无法加载数据而抛出异常。
解决办法:(两种)
1、在session关闭前调用一次上面的方法(person.getEmail()),强迫hibernate加载Email数据;session关闭后JDBC连接就关闭了,无法再从数据库读取任何数据了。
2、延迟session的范围,使session关闭前完成所有的业务逻辑。
单边的一对多关系
单边的一对多关系是指一方有集合属性,包含多个多方,而多方没有一方的引用。比较典型的是用户与电子邮件。其中,用户为一方,电子邮件为多方,一个用户可以拥有多个电子邮件记录。
Person.java中有id、name、List<Email>;
Email.java中有id、email;
一个person有多个email,而email中没有person;
对应的表中
tb_person:id、name;
tb_email:id、email、person_id;
若person中有email外键,则一个person的email外键要有多个id,不现实;而email中有person外键,一个email只需要有一个person的外键即可
总结:
一个person有多个email,则person类中有多个email,而person表中没有email外键,email表中有person外键;
具体实现如下:
Person.java
[html] view
plain copy
package com.lmb.hibernate.bean;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.OneToMany;
import javax.persistence.OrderBy;
import javax.persistence.Table;
@Entity
@Table(name="tb_person")
public class Person {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
//一对多配置,并配置列关系
<span style="color:#ff0000;">@OneToMany(fetch=FetchType.EAGER,
targetEntity=Email.class,
cascade={CascadeType.PERSIST,CascadeType.REMOVE,CascadeType.MERGE,CascadeType.REFRESH}
)
@JoinColumns(value = { @JoinColumn(name="person id",referencedColumnName="id") })
//配置排序方法
@OrderBy(value="email desc")
</span> private List<Email> emails=new ArrayList<Email>();
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;
}
public List<Email> getEmails() {
return emails;
}
public void setEmails(List<Email> emails) {
this.emails = emails;
}
}
Person实体中使用@OneToMany配置一对多关系。fetch配置加载方式(延迟加载fetch=FetchType.LAZY、即时加载fetch=FetchType.EAGER);targetEntity配置集合属性中的类型,由于emails属性类型为List<Email>,用反省制定了集合内为Email对象,因此targetEntity可省略;cascade配置级联方式,本例配置为PERSIST、REMOVE、MERGE、REFRESH,表示在保存、删除、修改、刷新Person类时会自动在数据库中保存、删除、修改、刷新属于它的Email对象。配置级联关系后,可以通过操作Person类来操作Email类。该配置也等同于CascadeType.ALL。@OrderBy配置排序方式,本例将Email按照email列降序排列。
Email实体类比较简单,由于是单边关系映射,Email中没有用户实体的引用,只是一个简单的POJO。
Email.java
[html] view
plain copy
package com.lmb.hibernate.bean;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
@Entity
@Table(name="tb_email")
public class Email {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Integer id;
private String name;
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;
}
}
配置完毕后要将Person类和Email类在hibernate配置文件中声明为实体类。实体类要配置在<session-factory />中。
hibernate.cfg.xml
[html] view
plain copy
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="connection.url">jdbc:mysql://localhost:3306/hibernate?characterEncoding=UTF-8</property>
<property name="connection.username">lmb</property>
<property name="connection.password">lmb</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<span style="color:#ff0000;"><mapping class="com.lmb.hibernate.bean.Person" />
<mapping class="com.lmb.hibernate.bean.Email" />
</span>
</session-factory>
</hibernate-configuration>
以上使用@注解实现的,当然我们还可以使用XML配置的方式实现,这了我就不在列出。
延迟加载与即时加载
即时加载(Eagar Fetching),查询Person的时候自动加载相关的Email
延迟加载(Lazy Fetching),只有在需要的时候才查询Email(默认)
延迟加载异常
一般来说,延时加载比即时加载节省资源。但是如果处理不当,延迟加载容易抛出延时加载异常(LazyInitializationException)。这是因为延迟加载时,只有第一次调用person.getEmail()才会加载emails数据,如果这时候数据库连接已经关闭了,就会因为无法加载数据而抛出异常。
解决办法:(两种)
1、在session关闭前调用一次上面的方法(person.getEmail()),强迫hibernate加载Email数据;session关闭后JDBC连接就关闭了,无法再从数据库读取任何数据了。
2、延迟session的范围,使session关闭前完成所有的业务逻辑。
相关文章推荐
- Hibernate实体映射——单边的一对多关系
- Hibernate实体关系映射——单边的多对一关系
- Hibernate实体关系映射——单边的多对一关系
- Hibernate实体关系映射——单边的一对一关系
- Hibernate实体关系映射(OneToMany单边)——完整实例
- Hibernate实体关系映射——单边的多对多关系
- Hibernate实体关系映射——单边的一对一关系
- Hibernate实体关系映射——单边的一对一关系
- 4、Hibernate 实体关系映射(二、单边多对一关系 注解、xml两种方式)
- hibernate实体关系映射——单边的多对多关系
- 4、Hibernate 实体关系映射(一、单边一对多关系 注解、xml两种方式)
- Hibernate实体关系映射——双边的多对多关系
- hibernate 实体关系映射笔记(转载自51)
- Hibernate之jpa实体映射的三种继承关系
- Hibernate实体关系映射(OneToMany、ManyToOne双边)——完整实例
- Hibernate实体关系关联映射中配置文件的编写
- 解决使用Hibernate QBC复合查询含有联合主键映射关系实体的问题
- Hibernate实体关系映射
- javaEE 用户、部门、角色、权限实体的关系设计与hibernate映射配置文件关系总结
- Hibernate(六):实体关系映射