Spring Hibernate 5 快速入门(二) 连表操作 OneToOne

1 概述

所有的代码都保证是可运行的完整project, 代码分享在github.com, 平时工作中也可以作为模板代码ctrl+c用.
https://github.com/ZhongjunTian/spring-hibernate-examples
本章内容在 intermediate-hibernate-1 文件夹下, 执行App.java即可运行例子.

最基本连表关系:OneToOne关系

一对一的关系是最基本的表与表之间的关系。
下面假设我们有3个表,分别为Person, Account, Address.
因为我们定义了他们之间的关系为一对一的关系,所以每个Person只有一个Account和一个Address.

2. Table定义

create table person_table (
        id bigint generated by default as identity,
        name varchar(255),
        account_id bigint
        );
create table account_table (
        id bigint generated by default as identity,
        balance decimal(19,2)
        );
create table address_table (
        id bigint generated by default as identity,
        city varchar(255),
        person_id bigint
        );

这里Person和Account之间的OneToOne关系, 由Person拥有account的id,也就是account_id,
而Person和Address之间的关系, 我们让address拥有person的id, 也就是person_id (这样做是为了展示两种情况的代码)

2.1 Person类

首先我们会创建三个@Entity类 Person.java Account.java Address.java
Person有如下内容

@Entity(name="PersonTable")
public class Person {
    @Id
    @GeneratedValue
    public Long id;

    public String name;
    
    //Person表拥有account_id,而Account表则没有person_id,这里与Address相反
    @OneToOne(cascade = CascadeType.ALL)
    public Account account;

    //Address表拥有person_id
    @OneToOne(mappedBy = "owner")
    @JoinColumn
    public Address address;
    ...
 }
@Entity.name

这里为了避免读者在下文中混淆java的Person类和Person表, 我特地让类和表的名字不一样. name标明这个Entity对应 Person_table表

@Id

主键

@OneToOne

表面Account是一个Entity, 并且Person和Account是一一对应的关系, 任何一个Person最多有一个Account.

cascade (可选)

级联(串联)操作, 默认是不执行任何级联操作.
什么是级联操作? 这里用伪代码更好解释,

    //假设我们从repository读取到了person   
    person = personRepository.find(1L);
    person.name = "李四"
    person.account.balance += 100;
    
    personRepository.save(person);
    personRepository.delete(person);

当你 更新/删除 person的时候, Hibernate并不能确定你是要操作person还是person和account两个一起操作. 有点类似于古代的连坐制度, 当person被xx的时候account也要被xx.
我们这里为了做个示范, 就直接级联所有操作了. 具体还有很多种, 常用的是Persist, Merge, Remove 和 All

而Address就没有设置cascade, 因此在使用的时候Person和address要分开保存, 详见最后面的public void oneToOneNoCascading1()

mappedBy

Mapped标明Person是无辜群众, 没有address的任何信息, 另一个Address表拥有外键(或者更专业的说法是 关系的拥有者为Address ).
并且由java的类Address.owner这个成员变量维护他们之间的关系.

隐式的Long accountId

这里有个小细节, 虽然PersonTable表里面有外键account_id但是Entity里面却没有,
实际上Hibernate会默认 public Account account; 的外键在PersonTable表里面为account_id.
如果这里是 public Account xx; 那么hibernate会认为PersonTable表里面有外键xx_id;

2.2 Account类

@Entity(name = "AccountTable")
public class Account {
    @Id
    @GeneratedValue
    public Long id;

    public BigDecimal balance;//余额
    ...
    }

因为Person是关系的拥有者, 所以这个类就很简单了.
并且, 我们没有在Account类里面定义Person成员, Hibernate官方文档里面称这种关系为 unidirectional,
另一种关系为bi-directional双向关系, Person和Address的关系就是双向的.

2.3 Address类

@Entity(name = "AddressTable")
public class Address {
    @Id
    @GeneratedValue
    public Long id;

    public String city;

    @OneToOne
    @JoinColumn(name = "person_id") //如果没有这个就会认为外键为 PersonTable 表的 owner_id列
    public Person owner;
    ...
}
@JoinColumn

标明外键的名字, 如果没有这个就会默认外键为AddressTable表的owner_id

3. 示范代码


@Service
public class Demo {
    @Autowired
    PersonRepository personRepository;
    @Autowired
    AccountRepository accountRepository;

    @Transactional  //one to one relationship
    public void oneToOneCascading1() {
        System.out.println("*******************开始");
        //先创建account
        Account account = Account.createAccount();

        //再把person绑定address
        Person person = Person.createPerson();
        person.account = account;

        person = personRepository.save(person);
        System.out.println("直接同时创建 person 和 account: "+person);//
    }

    @Transactional
    public void oneToOneCascading2() {
        System.out.println("*******************开始");
        //创建account
        Account account = Account.createAccount();
        account = accountRepository.save(account);

        System.out.println("先创建account: "+account);//
        //把person绑定address
        Person person = Person.createPerson();
        person.account = account;

        person = personRepository.save(person);
        System.out.println("再创建person, 同时绑定了前面的address: "+person);//
    }

    @Transactional
    public void oneToOneNocascading1() {
        System.out.println("*******************开始");
        //把person绑定address
        Person person = Person.createPerson();
        person = personRepository.save(person);
        System.out.println("先创建person: "+person);//

        //创建account
        Account account = Account.createAccount();
        account = accountRepository.save(account);
        System.out.println("再创建account: "+account);//

        person.account = account;
        person = personRepository.save(person);
        System.out.println("再更新person: "+person);//

    }

}

你可能感兴趣的:(Spring Hibernate 5 快速入门(二) 连表操作 OneToOne)