关联关系映射--概念:

关联关系是使用最多的一种关系,非常重要。在内存中反映为实体关系,映射到DB中为主外键关系。

实体间的关联,即对外键的维护。关联关系的发生,即对外键数据的改变。

外键:外面的主键,即,使用其它表的主键值作为自已的某字段的取值。

1) 基本概念:

  关联属性:Java代码的实体类定义中,声明的另一个实例类类型或其集合类型的属性,称为关联属性。

  关联关系维护:关联关系的维护,也称为外键维护,即为外键字段赋值。Hibernate默认情况下,关联的双方都具有维护权。即在代码中均可通过调用自己关联属性的set方法来建立关联关系。反映到数据库中,即是为外键字段赋值。

         不过,由于外键是建立在多方表中的,所以对于外键的维护方式,即为外键字段赋值的方式,一方维护与多方维护,其底层执行是不同的。

         若关联关系由一方维护,只能通过update语句来完成。若关联关系由多方维护,通过insert语句来完成。

         虽然双方均具有维护权,但一方同时具有放弃维护权的能力。通过对一方关联属性inverse=“true”设置,即可放弃关联关系维护权,将维护权完全交给多方。

  预处理语句:所谓预处理语句,即当前先不执行,等后面条件成熟,或程序运行完毕再执行的语句。当一方具有关联关系的维护权,并且执行save(一方对象)时,会产生一条update预处理语句,用于维护外键值。

        当多方具有关联关系的维护权,并且执行save(多方对象)时,会产生一条insert预处理语句,用于维护外键值。

  级联关系:当对某一类的对象a进行操作,如增加、删除、修改时,同时会自动对另一类的某对象b进行相同的操作。此时称,对象a、b具有级联关系,对象b为对象a的级联对象。

       级联操作是通过映射文件的cascade属性设置的。

       该属性的值较多,其介绍如下:

       none:在保存、更新或删除当前对象时,忽略其他关联的对象,即不使用级联。它是默认值。

       save-update:当通过Session的save()、update()、saveOrUpdate()方法来保存或更新当前对象时,将级联到其他DB中的相关联的表。

       delete:当通过Session的delete()方法删除当前对象时,将级联删除所有关联的对象。

       all:包含save-update及delete级联的所有行为。另外,当对当前对象执行lock()操作时,也会对所有关联的持久化对象执行lock()操作。

       delete-orphan:删除所有和当前对象解除关联关系的对象。

       all-delete-orphan:包含all和delete-orphan级联的所有行为。

  关联方向:(1)单向关联:指具有关联关系的实体对象间的加载与访问关系是单向的。即,只有一个实体对象可以加载和访问对方,但对方是看不到另一方的。

       (2)双向关联 :指具有关联关系的实体对象间的加载与访问关系是双向的。即,任何一方均可加载和访问另一方。

  关联数量:实体对象间的关系,从数量上可以划分为:1:1,1:n,n:1,m:n

总结底层执行:

  一对多关系中,一方维护关联关系,先插入多方数据,后插入一方数据,最后update多方表中的外键值(???是否正确);

  多方维护关联关系,先插入一方数据,后插入多方数据,在插入多方数据的同时插入外键值;

  多对多关系中,哪一方维护关联关系,就是哪一方数据先插入,再插入关联方数据,最后插入中间表数据。

建立(多对一、一对多)关系:

我们以用户和城市为例:

多对一:需要在 用户 User 实体类中定义一个 Address 类属性;

一对多:需要在 城市类中定义一个 Set<User> 集合元素属性;

User 用户类:

package com.mlq.bena;
import java.io.Serializable;
/**
* 用户类
* @author asus
*/
public class User implements Serializable {
private Integer uId;
private String userName;
private String userPwd;
private String realName;
private Address address;
private Integer dId;
@Override
public String toString() {
return "User{" +
"uId=" + uId +
", userName='" + userName + '\'' +
", userPwd='" + userPwd + '\'' +
", realName='" + realName + '\'' +
", address=" + address +
'}';
}
public Integer getdId() {
return dId;
}
public void setdId(Integer dId) {
this.dId = dId;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Integer getuId() {
return uId;
}
public void setuId(Integer uId) {
this.uId = uId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPwd() {
return userPwd;
}
public void setUserPwd(String userPwd) {
this.userPwd = userPwd;
}
public String getRealName() {
return realName;
}
public void setRealName(String realName) {
this.realName = realName;
}
}

Address 城市类:

package com.mlq.bena;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* 城市类
* @author asus
*/
public class Address implements Serializable {
private Integer dId;
private String addres;
private Set<User> list;
public Set<User> getList() {
return list;
}
public void setList(Set<User> list) {
this.list = list;
}
@Override
public String toString() {
return "Address{" +
"dId=" + dId +
", addres='" + addres + '\'' +
'}';
}
public Address() {
}
public Integer getdId() {
return dId;
}
public void setdId(Integer dId) {
this.dId = dId;
}
public String getAddres() {
return addres;
}
public void setAddres(String addres) {
this.addres = addres;
}
}

user.hbm.xml 文件主要内容:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
package: 需要映射的实体类所在包
class节点:
name:对应的是实体类的类名,如果没有书写package,则必须是完整限定类名 table:数据库中的表名,如果表名和实体类名一致,可以省略
id节点:表中的主键
generator:主键生成策略 ,主键由谁去创建?程序猿?Hibernate?数据库
name: 必须和实体类中的属性名一致
column : 必须和数据库中的列名一致,如果列名和属性名一致,可以省略
-->
<!--dynamic-update="false":默认非动态更新-->
<hibernate-mapping package="com.mlq.bena">
<class name="com.mlq.bena.User" table="`user`" dynamic-update="true">
<id name="uId" column="uId">
<!--<generator class="assigned"/>&lt;!&ndash;主键生成策略&ndash;&gt;-->
<generator class="increment"></generator>
</id>
<property name="userName" column="userName"/>
<property name="userPwd" column="userPwd"/>
<property name="realName" column="realName"/>
<!--多对一:给User内置对象映射-->
<many-to-one name="address" column="`dId`" class="com.mlq.bena.Address" />
</class>
</hibernate-mapping>
<!--Oracle序列增长-->
<!--<generator class="squence">-->
<!--<param name="squence">SEQ_DEPTNO</param>-->
<!--</generator>-->
<!--数据库中查询最大值+1-->
<!--<generator class="increment"></generator>-->
<!--没有制定使用那种方式:需要结合数据库方言自增-->
<!--<generator class="native"></generator>-->
<!--uuid:生成32位字符串-->
<!--<generator class="uuid"></generator>-->

address.hbm.xml 主要配置内容:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
package: 需要映射的实体类所在包
class节点:
name:对应的是实体类的类名,如果没有书写package,则必须是完整限定类名 table:数据库中的表名,如果表名和实体类名一致,可以省略
id节点:表中的主键
generator:主键生成策略 ,主键由谁去创建?程序猿?Hibernate?数据库
name: 必须和实体类中的属性名一致
column : 必须和数据库中的列名一致,如果列名和属性名一致,可以省略
-->
<!--dynamic-update="false":默认非动态更新-->
<hibernate-mapping package="com.mlq.bena">
<class name="com.mlq.bena.Address" table="`address`" dynamic-update="true">
<id name="dId" column="dId">
<generator class="increment"></generator>
</id>
<property name="addres" column="addres"/>
<set name="list">
<key column="dId"></key>
<one-to-many class="com.mlq.bena.User"/>
</set>
</class>
</hibernate-mapping>

<many-to-one>元素建立了User表的外键 dId 和 address属性之间的映射。它包括以下属性:

1):name:设定持久化类的属性名,此处为User类的address属性。

2):column:设定持久化的属性对应的外键,此处为 user 表的外键 dId。

3):class:设定持久化类的属性的类型,此处设定address 为Address类型。

到此,User类到Address类的单向多对一映射就完成了。

<set>元素的name属性:设定持久化的属性名,此处为Address类的user 属性。

<set>元素:还包含两个子元素:

  <key>元素:cloumn 属性设定与所关联的持久化类相对应的表的外键,此处为User 表的did

<one-to-many>元素:class属性设定所关联的持久化类型,此处为 User类

Hibernate 根据以上映射代码获得以下信息:

<set> 元素表明 Address 类的 list 属性为 java.uitl.Set 集合类型。

<one-to-many> 子元素表明 list 集合中存放的是一组 User 对象。

<key>子元素表明 User 表通过外键 dId参照 Address 表。

双向关联下的级联操作:cascade 属性:

级联(cascade):

  当Hibernate持久化一个临时对象时,在默认情况下,它不会自动持久化所关联的其他临时对象,而是会抛出TransientObjectException。如果设定many-to-one元素的cascade属性为save-update的话,可实现自动持久化所关联的对象。

如:

<many-to-one
name="customer"
column="CUSTOMER_ID"
class="..Customer"
cascade="save-update"
not-null="true"
/>

级联指的是当主控方执行操作时,关联对象(被动方)是否同步执行同一操作。

Inverse 属性:(维护主外键关系权)

inverse属性 :

  inverse所描述的是对象之间关联关系的维护方式。

  inverse只存在于集合标记的元素中 。

  Hibernate提供的集合元素包括<set/> <map/> <list/> <array /> <bag />

  Inverse属性的作用是:是否将对集合对象的修改反映到数据库中。

  inverse属性的默认值为false,表示对集合对象的修改会被反映到数据库中;inverse=false 的为主动方,由主动方负责维护关联关系。

  inverse=”true” 表示对集合对象的修改不会被反映到数据库中。

为了维持两个实体类(表)的关系,而添加的一些属性,该属性可能在两个实体类(表)或者在一个独立的表里面,这个要看这双方直接的对应关系了: 这里的维护指的是当主控放进行增删改查操作时,会同时对关联关系进行对应的更新。

一对多:

  该属性在多的一方。应该在一方的设置 inverse=true ,多的一方设置 inverse=false(多的一方也可以不设置inverse属性,因为默认值是false),这说明关联关系由多的一方来维护。

  如果要一方维护关 系,就会使在插入或是删除"一"方时去update"多"方的每一个与这个"一"的对象有关系的对象。

  而如果让"多"方面维护关系时就不会有update 操作,因为关系就是在多方的对象中的,直指插入或是删除多方对象就行了。显然这样做的话,会减少很多操作,提高了效率。

注:

  单向one-to-many关联关系中,不可以设置inverse="true",因为被控方的映射文件中没有主控方的信息。

多对多:

   属性在独立表中。inverse属性的默认值为false。在多对多关联关系中,关系的两端 inverse不能都设为false,即默认的情况是不对的,如果都设为false,在做插入操作时会导致在关系表中插入两次关系。

  也不能都设为 true,如果都设为true,任何操作都不会触发对关系表的操作。因此在任意一方设置inverse=true,另一方inverse=false。

一对一:

  其实是一对多的一个特例,inverse 的设置也是一样的,主要还是看关联关系的属性在哪一方,这一方的inverse=false。

多对一:

  也就是一对多的反过来,没什么区别。

 例:

建立多对多关联关系:

多对多就是需要在两个实体类中都存在一个Set集合元素。同时需要一个中间表维护两者的关系。(共三张表)这里以老师和学生为例:

学生表:

中间表:

老师表:

实体类:Teacher

package com.mlq.bena;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Teacher implements Serializable{
private String name;
private Integer tid;
private Set<Student> students=new HashSet<Student>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getTid() {
return tid;
}
public void setTid(Integer tid) {
this.tid = tid;
}
public Set<Student> getStudents() {
return students;
}
public void setStudents(Set<Student> students) {
this.students = students;
}
}

实体类:Student

package com.mlq.bena;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Student implements Serializable { private Integer sid;
private String sname;
private Integer age;
private Set<Teacher> teachers = new HashSet<Teacher>();
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Set<Teacher> getTeachers() {
return teachers;
}
public void setTeachers(Set<Teacher> teachers) {
this.teachers = teachers;
}
}

 提示只需要两张表的实体类就可以:中间表不需要。

 核心映射文件:Student.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
package: 需要映射的实体类所在包
class节点:
name:对应的是实体类的类名,如果没有书写package,则必须是完整限定类名 table:数据库中的表名,如果表名和实体类名一致,可以省略
id节点:表中的主键
generator:主键生成策略 ,主键由谁去创建?程序猿?Hibernate?数据库
name: 必须和实体类中的属性名一致
column : 必须和数据库中的列名一致,如果列名和属性名一致,可以省略
-->
<!--dynamic-update="false":默认非动态更新-->
<hibernate-mapping package="com.mlq.bena">
<class name="com.mlq.bena.Student" table="`student`" dynamic-update="true">
<id name="sid" column="sid">
<generator class="increment"></generator>
</id>
<property name="sname" column="sname"/>
<property name="age" column="age"/>
<!--cascade级联操作-->
<!--inverse:默认false,执行外键的update验证,true:不执行update维护外键-->
<set name="teachers" table="middle" cascade="save-update,delete" inverse="true">
<key column="sid"></key>
<many-to-many column="tid" class="com.mlq.bena.Teacher"/>
</set>
</class>
</hibernate-mapping>

 核心映射文件:Teacher.hbm.xml:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--
package: 需要映射的实体类所在包
class节点:
name:对应的是实体类的类名,如果没有书写package,则必须是完整限定类名 table:数据库中的表名,如果表名和实体类名一致,可以省略
id节点:表中的主键
generator:主键生成策略 ,主键由谁去创建?程序猿?Hibernate?数据库
name: 必须和实体类中的属性名一致
column : 必须和数据库中的列名一致,如果列名和属性名一致,可以省略
-->
<!--dynamic-update="false":默认非动态更新-->
<hibernate-mapping package="com.mlq.bena">
<class name="com.mlq.bena.Teacher" table="`teacher`" dynamic-update="true">
<id name="tid" column="tid">
<!--<generator class="assigned"/>&lt;!&ndash;主键生成策略&ndash;&gt;-->
<generator class="increment"></generator>
</id>
<property name="name" column="name"/>
<!--cascade级联操作-->
<!--inverse:默认false,执行外键的update验证,true:不执行update维护外键-->
<set name="students" table="middle" cascade="save-update,delete" inverse="false">
<key column="tid"></key>
<many-to-many class="com.mlq.bena.Student" column="sid"/>
</set>
</class>
</hibernate-mapping>

注意:虽然中间表不需要实体类,但是在我们配置文件时,要写上 table 属性,来告诉它这是多对多关联。table 属性值就是中间表。

    注意关联的字段名称保持一致。

Lazy 属性:延迟加载:

一.延迟加载的概念

  当Hibernate从数据库中加载某个对象时,不加载关联的对象,而只是生成了代理对象,获取使用session中的load的方法(在没有改变lazy属性为false的情况下)获取到的也是代理对象,所以在上面这几种场景下就是延迟加载。

二.理解立即加载的概念

  当Hibernate从数据库中加载某个对象时,加载关联的对象,生成的实际对象,获取使用session中的get的方法获取到的是实际对象。

三.为什么要使用延迟加载

  延迟加载策略能避免加载应用程序不需要访问的关联对象,以提高应用程序的性能。

四.立即加载的缺点

  Hibernate在查询某个对象时,立即查询与之关联的对象,我们可以看出这种加载策略存在两大不足:

  1.select的语句数目太多,需要频繁的访问数据库,会影响查询的性能。

  2.在应用程序只需要访问要的对象,而不需要访问与他关联的对象的场景下,加载与之关联的对象完全是多余的操作,这些多余的操作是会占内存,这就造成了内存空间的浪费。

五.什么时候使用延迟加载什么时候使用立即加载

  如果程序加载一个持久化对象的目的是为访问他的属性,则可以采用立即加载。如果程序加载一个持久化对象的目的仅仅是为了获得他的引用,则可以采用延迟加载。

六.Hibernate在对象-关系映射问价中配置加载策略

  I.类级别:

    <class>元素中lazy属性的可选值为 true (延迟加载)和 false (立即加载);

    <class>元素中的lazy属性的默认值为true

  II.一对多关联级别:

    <set>元素中的lazy属性的可选值为:

      true(延迟加载)

      extra(增强延迟加载)

      false(立即加载);

    <set>元素中的lazy属性的默认值为true

  III.多对一关联级别:

    <many-to-one>元素中lazy属性的可选值为:

      proxy(延迟加载)

      no-proxy(无代理延迟加载)

      false(立即加载)

    <many-to-one>元素中的lazy属性的默认值为proxy

Open Session in View 的使用:

为什么要使用 Open Session in View ?

因为:默认是懒加载机制,当我们去操作查询出来的数据时,会给我们报 会话已断开。(例如调用一个类中关联的 类属性)

解决方式:延迟会话关闭时间。(调用完在关闭)增加过滤器拦截请求。(把之前在Session中关闭会话的代码 删掉就 OK 了

配置如下:

Web.XML 中添加以下配置:

<!--OpenSessionInVie会话配置-->
<filter>
<filter-name>openSessionInview</filter-name>
<filter-class>com.mlq.uitl.Fileter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInview</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

Fileter 自定义拦截器类:

package com.mlq.uitl;
import org.hibernate.HibernateException;
import org.hibernate.Transaction;
import javax.servlet.*;
import java.io.IOException;
public class Fileter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化------");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
Transaction transaction=null;
try{
//请求达到时,打开Session并启动事务
transaction=SessionTool.getSession().beginTransaction();
//执行请求处理链
chain.doFilter(request,response);
//返回响应时,提交事务
transaction.commit();
}catch (HibernateException ex)
{
System.out.println("=======异常");
ex.printStackTrace();
transaction.rollback();
}
}
@Override
public void destroy() {
System.out.println("销毁--------------");
}
}

分享知识-快乐自己:Hibernate 关联映射的更多相关文章

  1. Hibernate关联映射关系

    Hibernate关联映射关系 一.双向一对多关联映射关系:当类与类之间建立了关联,就可以方便的从一个对象导航到另一个或另一组与它关联的对象(一对多双向关联和多对一双向关联是完全一样的) 1.1创建实 ...

  2. Oracle primary,unique,foreign 区别,Hibernate 关联映射

    Oracle primary,unique,foreign 区别 转:http://www.cnblogs.com/henw/archive/2012/08/15/2639510.html NOT N ...

  3. 第六章 Hibernate关联映射

    第六章 hibernate关联映射一.本章知识点分为2部分:1.关联关系:单向多对一关联关系,双向一对多关联关系(含一对多关联关系),多对多关联关系2.延迟加载:类级别加载策略,一对多加载策略,多对一 ...

  4. 【学习笔记】Hibernate关联映射(Y2-1-6)

    Hibernate关联映射 关联映射就是将关联关系映射到数据库里,在对象模型中就是一个或多个引用. 1.单向多对一关联 准备数据库 部门表和员工表 其中部门表有两列 部门编号和名称 员工表有三列 员工 ...

  5. 第三章Hibernate关联映射

    第三章Hibernate关联映射 一.关联关系 类与类之间最普通的关系就是关联关系,而且关联是有方向的. 以部门和员工为列,一个部门下有多个员工,而一个员工只能属于一个部门,从员工到部门就是多对一关联 ...

  6. (转)Hibernate关联映射——对象的三种关系

    http://blog.csdn.net/yerenyuan_pku/article/details/70148618 Hibernate关联映射——对象的三种关系 Hibernate框架基于ORM设 ...

  7. (转)Hibernate关联映射——一对多(多对一)

    http://blog.csdn.net/yerenyuan_pku/article/details/70152173 Hibernate关联映射——一对多(多对一) 我们以客户(Customer)与 ...

  8. Hibernate关联映射(一对多/多对多)

    版权声明:翀版 https://blog.csdn.net/biggerchong/article/details/843401053.  Hibernate关联映射上接Hibernate持久化类:h ...

  9. Java三大框架之——Hibernate关联映射与级联操作

    什么是Hibernate中的关联映射? 简单来说Hibernate是ORM映射的持久层框架,全称是(Object Relational Mapping),即对象关系映射. 它将数据库中的表映射成对应的 ...

  10. Hibernate关联映射 映射文件的配置

    一:多对一单向关联 首先我们必须创建两个实体类 例如:Dept类 public class Dept { private Integer deptNo; private String dName; p ...

随机推荐

  1. [JS][jQuery]清空元素html(&quot;&quot;)、innerHTML=&quot;&quot; 与 empty()的差别:关于内容泄露问题

    清空元素html("").innerHTML="" 与 empty()的差别 一.清空元素的差别      1.错误做法一:            $(&quo ...

  2. 【Python】随机漫步

    创建Randomwalk()类 我们将使用Python来生成随机漫步数据,再使用matplotlib以引入瞩目的方式将这些数据呈现出来 首先创建类Randomwalk() from random im ...

  3. 统一建模语言(UML,Unified Modeling Language)

    Something about UML: 统一建模语言(UML,英语:Unified Modeling Language)是非专利的第三代建模和规约语言.UML是一种开放的方法,用于说明.可视化.构建 ...

  4. Javascript模式(一) 单例模式

    function A(){ // 存储实例对象 var instance; // 重写构造函数,只返回闭包内的局部变量instance A = function(){ return instance; ...

  5. wcf上传字节数组报错问题

    为了实现上传大文件所以我们要如下设置最大值,其中security是设置访问服务的认证,此处是把它设置成为不认证,transferMode就是设置运用流的模式 <webHttpBinding> ...

  6. javascript判断智能终端信息

    < script type = "text/javascript" > /* * 智能机浏览器版本信息: * */ var browser = { versions: ...

  7. 本地aar文件引用

    有时须要使用第三方的aar库.或是project源码越来越大.项目内分工须要或出于模块化考虑.须要引用aar文件. arr就像C/C++中的静态库. 怎样建一个aar.网上的文章非常多,这里不再重述. ...

  8. APU的Vsense引脚的作用

    JACK学习文档推荐: 开关电源PCB布局注意事项 开关电源PCB布线注意事项 一.Sense电压检测(FB) “Sense+”和“Sense-”,就是四线制中的电压检测线,high-sense 和l ...

  9. HBase——完全分布

    实际上,在真实环境中你需要使用完全分布配置完整测试HBase.在一个分布式配置中,集群有多个节点,每个节点运行一个或多个HBase守护进程.其中包括主Master和备份Master实例,多个Zooke ...

  10. erlang中判断进程是否存活

    一个参数的方法是已知Pid判断进程是否存活.两个参数的方法是已知节点和Pid或进程名判断进程是否存活. is_process_alive(Pid) when is_pid(Pid)->rpc:c ...