关联关系映射--概念:

关联关系是使用最多的一种关系,非常重要。在内存中反映为实体关系,映射到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. metaq入门部署到实战

    初识metaq zookeeper部署,这里单机zk为例. wget http://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.4.5/zookeep ...

  2. socket 、 udp 和 tcp

    强调一点: socket . udp . tcp之间的区别. socket只是一种套接字,就是两台电脑两端的东西,中间传输以流的格式进行.  IBEO好像是TCP/IP ,  无论对于TCP和UDP, ...

  3. struts2的BaseAction<T>继承ActionSupport实现ModelDriven<T>

    public class BaseAction<T> extends ActionSupport implements ModelDriven<T> { private sta ...

  4. Struts2学习一----------Struts2的工作原理及HelloWorld简单实现

    © 版权声明:本文为博主原创文章,转载请注明出处 Struts2工作原理 一个请求在Struts2框架中的处理步骤: 1.客户端初始化一个指向Servlet容器(例如Tomcat)的请求 2.这个请求 ...

  5. FZU2125:简单的等式

    Problem Description 如今有一个等式例如以下:x^2+s(x,m)x-n=0. 当中s(x,m)表示把x写成m进制时,每一个位数相加的和.如今,在给定n,m的情况下,求出满足等式的最 ...

  6. XFire Web Service客户端开发

    一.项目创建: 创建一个Maven的web工程 Maven包导入pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0&qu ...

  7. nyist oj 37 回文字符串 (动态规划经典)

    回文字符串 时间限制:3000 ms  |  内存限制:65535 KB 难度:4 描写叙述 所谓回文字符串,就是一个字符串.从左到右读和从右到左读是全然一样的.比方"aba".当 ...

  8. linux下网卡绑定

    网卡绑定的作用:1.冗余,防止单点故障 2.防止传输瓶颈 1.交换机端口绑定: system-view link-aggregation group 1 mode manual 比如把端口1和2进行绑 ...

  9. 【转载】【selenium+Python WebDriver】之元素定位

    总结: 感谢: “煜妃”<Selenuim+Python之元素定位总结及实例说明> “Huilaojia123”<selenium WebDriver定位元素学习总结> “上海 ...

  10. PythonCookBook笔记——函数

    函数 可接受任意数量参数的函数 接受任意数量的位置参数,使用*参数. 接受任意数量的关键字参数,使用**参数. 只接受关键字参数的函数 强制关键字参数放在某个参数后或直接单个之后. 给函数参数增加元信 ...