QQA: Hibernate 为什么需要手工管理双向关联

栏目: Hibernate · 发布时间: 6年前

内容简介:Hibernate/JPA 中如果两个 Entity 之间的关联是双向的(不论是双向关联的本质是告诉 Hibernate 让两个实体共用一张数据库表(或表结构)。这里以

Hibernate/JPA 中如果两个 Entity 之间的关联是双向的(不论是 @ManyToMany@OneToMany 还是 @OneToOne ),都需要手动管理关联,为什么?

  • 调用 entityManager.persist 保存对象时 Hibernate/JPA 不会直接执行 SQL,而会等到 entityManager.flush 或事务 commit 时完成。
  • 同理 entityManager.load 也可能只会从内存中获取对象(可以认为是某种缓存)。
  • 如果不手动管理双向关联,则从内存获取的对象并不会反映数据库中的映射关系。

双向关联的本质是告诉 Hibernate 让两个实体共用一张数据库表(或表结构)。

这里以 @ManyToMany 为例(参考 Hibernate User Guide ) :有两个实体 PersonAddress ,一个 Person 可以拥有多个 Address,而一个 Address 也可以属于多个 Person。于是设计实体如下:

@Entity
public static class Person {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany
    private List<Address> addresses = new ArrayList<>();

    // ... omit all other stuff
}

@Entity
public static class Address {
    @Id
    @GeneratedValue
    private Long id;

    @ManyToMany
    private List<Person> owners = new ArrayList<>();

    // ... omit all other stuff
}

问题来了,我们应该创建一张关联表还是两张呢?其实取决于使用业务含义。即如果 Personaddresses 的含义是“人的居住地址”,而 Address 中的 owners 与之对应,表达的是“地址上居住的人”,则它们应该是一张关联表。但如果 Addressowners 表达的是“地址的主人(如房东)”,则二者就不应该共用一张关联表。

如何告诉 Hibernate 需要共用一张表呢?通过 mappedBy

@Entity
public static class Person {
    @ManyToMany
    private List<Address> addresses = new ArrayList<>();
    // ... omit all other methods
}

@Entity
public static class Address {
    @ManyToMany(mappedBy = "addresses")
    private List<Person> owners = new ArrayList<>();

    // ... omit all other methods
}

(mappedBy = "addresses") 的含义是这个字段与 Person 中的 addresses 字段共用表结构。

这里最后重点是双向关系一定是从属关系,有一方是 owner,另一方是 follower(标记了 mappedBy 的一方)。只有在 owner 这方添加关联并保存时,Hibernate 才会存入关联表,反之不会。例如我们只能通过 person.addAddress() 并保存 person 的方式来完成添加关联而不能用 address.addPerson() 后保存 address 的方式。

手工管理关联是什么意思

例如我们在实现 Person.addAddress 时,需要这样实现:

@Entity
public static class Person {
    //...omit other fields

    @ManyToMany
    private List<Address> addresses = new ArrayList<>();

    public void addAddress(Address address) {
        addresses.add( address );
        address.getOwners().add( this );
    }

    public void removeAddress(Address address) {
        addresses.remove( address );
        address.getOwners().remove( this );
    }
    // ... omit all other methods
}

即在为 person 添加 address 时,我们需要将当前的 person 添加到 address的 owners 字段中;删除时相似。“管理关联”表示需要在代码级别来管理关联双方实体的联系。

如果从数据库的角度思考,我们知道 PersonAddress 的关系是存储在一张关联表里的,一个关联存入这张表后,不论哪一方读取,都应该反映出新的关联关系,而在 Hibernate 这一层,却需要我们显式地(从另一方的 set )中添加/删除这个关联,显得不可思议。

另外,注意我们往 set 中添加 addressperson 时,需要我们正确的实现 PersonAddressequalshashCode 方法,这是另一个坑,这里就不深入了。

为什么需要手工管理

终于到了“为什么”部分了,首先是如果不手工管理会发生什么。考虑下面的测试:

@Test
@Transactional
public void test() {
    Person person = repository.findPersonById(1);
    Address address = repository.findAddressById(20);
    person.getAddresses.add(address);
    repository.save(person);

    System.out.println(address.getOwners().size()) // what is the result?

    Address address = repository.findAddressById(20);
    System.out.println(address.getOwners().size()) // what is the result?
}

答案是两个 size 都为 0

save
find

注意上面说的是一般的情况,什么时候执行 SQL 取决于具体的配置,一般会在事务前的 commit

因此,如果在 save 之后还需要使用到 address ,就不要期待它会立即反映出数据库中的修改;反之,如果 save 之后就不再使用到 address ,那即使不手工管理(同步) 关联关系也不会有多大影响。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

计算机动画的算法基础

计算机动画的算法基础

鲍虎军 金小刚 彭群生 / 浙江大学出版社 / 2000-12 / 60.00元

《计算机应用技术前沿丛书:计算机动画的算法基础》主要内容简介:20世纪是一个科技、经济空前发展的时代,从世纪初相对论、量子理论的创立到今天以信息产业为龙头的高科技产业成为经济发展的第一支柱,人类社会发生了根本性的变革。而在这场以科学技术为社会发展直接动因的变革中,意义最深远、影响最广泛的就是计算机及其相关技术的发展和应用。在过去的50年里,计算机已从最初的协助人类进行精密和复杂运算的单一功能的运算......一起来看看 《计算机动画的算法基础》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具