Hibernate学习之---Hibernate的关联映射

1.单向N-1关联

单向的N-1关联只需从N的一端可以访问1的一端。
为了让两个持久化类支持这种关联映射,程序应该在N的一端的持久化类中增加一个属性,该属性引用1的一端的关联实体。
对于N-1关联,不管是单向还是双向,都需要在N的一端使用@ManyToOne修饰代表关联实体的属性。

(1.)无连接表的N-1关联
对于无连接表的N-1关联而言,程序只要在N的一端增加一列外键,让外键值记录该对象所属的实体即可。直接使用@JoinColumn注解来映射N-1关联时,Hibernate将无须使用连接表,直接使用外键关联策略来处理这种关联映射。

    @Entity
    @Table(name="person")
    public class Person{
        @ManyToOne(targetEntity=Address.class)
        @JoinColumn(name="address_id",nullable=false)
        @Cascade(CascadeType.ALL)
        private Address address;
    }

@Cascade注解,该注解用于指定级联操作策略,此处指定的级联策略是CascadeType.ALL,这表明对Person实体的所有持久化操作都会联级到它关联的Address实体。系统将先自动级联插入主表记录,也就是先持久化Address对象,再持久化Person对象。

在所有既有的基于外键约束的关联关系中,都必须牢记:要么总是先持久化主表记录对应的实体,要么设置级联操作,否则当Hibernate试图插入从表记录时,如果发现该从表记录参照的主表记录不存在,那一定会抛出异常。

(2.)有连接表的N-1关联
如果需要使用连接表来映射单向N-1关联,程序需要显示使用@JoinTable注解来映射连接表。

    @Entity
    @Table(name="person")
    public class Person{
        @ManyToOne(targetEntity=Address.class)
        @JoinTable(name="person_address",joinColumns=@JoinColumn(name="person_id",referencedColumnName="person_id",unique=true),inverseJoinColumns=@JoinColumn(name="address_id",referencedColumnName="address_id")
        )
    }

在这种映射策略下,person_id列既作为外键列参照person表的person_id主键列,也作为person_address连接表的主键列,这就保证了person_address数据表中的person_id列不能出现重复值,即保证了一个Person实体最多只能关联一个Address实体。

2.单向1-1关联

对于单向的1-1关联关系,需要在持久化类里增加代表关联实体的成员变量,并为该成员变量增加setter和getter方法。
对于1-1关联,不管是单向关联,还是双向关联,都需要使用@OneToOne修饰。

  • 基于外键的单向1-1关联
    先使用@OneToOne注解修饰代表关联实体的属性,再使用@JoinColumn映射外键列即可—由于是1-1关联,因此应该为@JoinColumn增加unique=true。
3.单向1-N关联

单向1-N关联的持久化类发生了改变,持久化类里需要使用集合属性。因为1的一端需要访问N的一端,而N的一端将以集合(Set)形式表现。从这个意义上来看,1-N(实际上还包括N-N)和前面的集合属性非常相似,只是此时集合里的元素时关联实体。
对于单向1-N关联关系,只需要在1的一端增加Set类型的成员变量,该成员变量记录当前实体所有的关联实体,当然还要为这个Set类型的属性增加setter和getter方法。
需要使用@OneToMany注解。

(1.)无连接表的单向1-N关联

    @Entity
    @Table(name="person")
    public class Person{
        @OneToMany(targetEntity=Address.class)
        @JoinColumn(name="person_id",referencedColumnName="person_id")
        private Set<Address> address = new HashSet<>();

    }

此处的外键列并不是增加到当前实体对应的数据表中,而是增加到关联实体的Address对应的数据表中。

如果程序在@OneToMany注解中指定了cascade=CascadeType.ALL,则Hibernate先执行insert into person…语句插入一条person记录,然后执行insert into address…语句插入一条address记录,再执行一条update address …语句来修改刚刚插入的address记录。如果没有指定,程序对Person实体的持久化操作不会级联到关联的Address实体,程序必须显示持久化Address实体。
使用这种关联关系,Address实体并不知道它所关联的Person实体,因为Hibernate无法在执行insert into address_inf…语句时为外键列指定值。解决办法为:
程序必须在持久化Address实体之前,让Address实体能知道它所关联的Person实体,也就是应该通过address.setPerson(person);方法来建立关联关系—这就需要把这个关联关系添加到Address的映射中—这就变成了双向1-N关联。因此应该尽量少用单向1-N单向关联,而是改为使用双向1-N关联。
万一程序必须采用单向1-N的关联模型,也应该采用有连接表的单向1-N关联。

4.单向N-N关联

单向的N-N关联和1-N关联的持久化类代码完全相同,控制关系的一端需要增加一个Set类型的属性,被关联的持久化实例以集合形式存在。
N-N关联必须使用连接表,但要去掉@JoinTable注解的inverseJoinColumns属性所指定的@JoinColumn中的unique=true。

5.双向1-N关联

不让1的一端控制关联关系,而使用N的一端控制关联关系。两端都需要增加对关联属性的访问,N的一端增加引用到关联实体的属性,1的一端增加集合属性,集合元素为关联实体。

(1.)无连接表的双向1-N关联
N的一端需要增加@ManyToOne注解来修饰代表关联实体的属性,而1的一端则需要使用@OneToMany注解来修饰代表关联实体的属性。
底层数据库为了记录这种1-N关联关系,实际上只需要在N的一端的数据表里增加一个外键列即可,因此应该在使用@ManyToOne注解的同时,使用@JoinColumn来映射外键列。
对于双向的1-N关联映射,通常不应该允许1的一端控制关联关系,而应该由N的一端来控制关联关系。因此应该在使用@OneToMany注解时指定mappedBy属性,表明当前实体不能控制关联关系,之后Hibernate即不允许使用@JoinColumn或@JoinTable修饰代表关联实体的属性了。

(2.)有连接表的双向1-N关联
1的一端无须任何改变,只要在N的一端使用@JoinTable显示指定连接表即可。

6.双向N-N关联

双向N-N关联需要两端都使用Set集合属性,只能采用连接表来建立两个实体之间的关联关系。
如果程序希望某一端放弃控制关联关系,则可在这一端的@ManyToMany注解中指定mappedBy属性,这一端就无须、也不能使用@JoinTable映射连接表了。

7.双向1-1关联

双向1-1关联需要让两个持久化类都增加引用关联实体的属性,并为该属性提供setter和getter方法。

(1.)基于外键的双向1-1关联
外键可以存放在任意一端,存放外键的一端,都需要增加@JoinColumn注解来映射外键列,还应该增加unique=true属性来表示该实体实际上是1的一端。
当选择任意一端来增加外键后,该表即变成从表,而另一个表则变成主表。
对于双向1-1关联的主表对应的实体,也不应该用于控制关联关系,否则会导致生成额外的update语句,从而引起性能下降。

8.组件属性包含的关联实体
    @Entity
    @Table(name="person")
    public class Person{
        private Address address;
    }

    @Embeddable
    public class Address{
        @OneToMany(targetEntity=School.class)
        @JoinColumn(name="address_id",referencedColumnName="person_id")
        private Set<School> schools = new HashSet<>();
    }

此处建立的是Address与School的单向1-N关联。
由于Address本身并不是持久化实体,因此无法让School建立与Address的关联关系,所以只能建立单向的。
School类是一个简单的持久化类,School类里无法定义访问Address类的关联属性,因为如果想让School类和Address类建立关联,那就要求把Address类也映射成持久化类。由于此处只是把Address当成组件属性使用,因此School无法和这个并不存在的持久化类建立关联。
一般来说,如果需要让持久化实体和组件属性建立关联关系,程序应该将该组件映射成持久化实体,而不是组件属性。

对于级联策略的设定,Hibernate有如下建议:

  • 在@ManyToOne中指定级联没什么意义。级联通常在@OneToOne和@OneToMany关系中比较有用—因为级联操作应该是由主表记录传播到从表记录,通常从表记录则不应该传播到主表记录。
  • 如果从表记录被完全限制在主表记录之内(当主表记录被删除后,从表记录没有存在的意义),则可以指定cascade=Cascade.ALL,再配合orphanRemoval=true级联策略,将从表实体的生命周期完全交给主表实体管理。
  • 如果经常在某个事物中同时使用主表实体和从表实体,则可以考虑指定cascade={CascadeType.PERSIST,CascadeType.MERGE}级联策略。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值