Relacionamento individual na JPA

Relacionamento individual na JPA

1. Introdução

Neste tutorial, veremos diferentes maneiras de criar mapeamentos um para um em JPA.

Precisamos de um conhecimento básico da estrutura do Hibernate, portanto, verifique nossoGuide to Hibernate 5 with Spring para informações adicionais.

Leitura adicional:

Visão geral dos tipos de cascata JPA / Hibernate

Uma visão geral rápida e prática dos tipos de cascata JPA / Hibernate.

Read more

Tutorial de hibernação de um para muitos

Neste tutorial, daremos uma olhada no mapeamento um-para-muitos usando anotações JPA com um exemplo prático.

Read more

2. Descrição

Suponhamos que estejamos construindo um Sistema de Gerenciamento de Usuários e nosso chefe nos peça para armazenar um endereço de correspondência para cada usuário. Um usuário terá um endereço para correspondência e um endereço para correspondência terá apenas um usuário vinculado a ele.

Este é um exemplo de relacionamento um a um, neste caso entreuser andaddress entidades.

Vamos ver como podemos implementar isso nas seções subsequentes.

3. Usando uma chave estrangeira

3.1. Modelando com uma chave estrangeira

Vamos dar uma olhada no seguinteER diagram que representa um mapeamento um para um baseado em chave estrangeira:

An ER Diagram mapping Users to Addresses via an address_id foreign key

Neste exemplo, a colunaaddress_id emusers é aforeign key aaddress.

3.2. Implementando com uma chave estrangeira na JPA

Primeiro, vamos criar oUser class e anotá-lo apropriadamente:

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    //...

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;

    // ... getters and setters
}

Observe quewe place the @OneToOne annotation no campo de entidade relacionado,Address.

Além disso,we need to place the @JoinColumn annotation para configurar o nome da coluna na tabelausers que mapeia para a chave primária na tabelaaddress. Se não fornecermos um nome, o Hibernate iráfollow some rules para selecionar um padrão.

Finalmente, observe na próxima entidade que não usaremos a anotação@JoinColumn lá. Isso ocorre porque só precisamos dele no ladoowning do relacionamento da chave estrangeira. Simply put, whoever owns the foreign key column gets the @JoinColumn annotation.

AAddress entidade acaba sendo um pouco mais simples:

@Entity
@Table(name = "address")
public class Address {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;
    //...

    @OneToOne(mappedBy = "address")
    private User user;

    //... getters and setters
}

Também precisamos colocar a anotação@OneToOne an aqui. Isso porque este é umbidirectional relationship. The address side of the relationship is called the non-owning side. 

4. Usando uma chave primária compartilhada

4.1. Modelando com uma chave primária compartilhada

Nesta estratégia, em vez de criar uma nova colunaaddress_id, we’ll mark the primary key column (user_id) of the address table as the foreign key to the users table:

An ER diagram with Users Tied to Addresses where they share the same primary key values

Otimizamos o espaço de armazenamento, utilizando o fato de que essas entidades têm uma relação um-para-um entre elas.

4.2. Implementando com uma Chave Primária Compartilhada na JPA

Observe que nossas definições mudam apenas um pouco:

@Entity
@Table(name = "users")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    private Address address;

    //... getters and setters
}
@Entity
@Table(name = "address")
public class Address {

    @Id
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne
    @MapsId
    private User user;

    //... getters and setters
}

@MapsId tells Hibernate to use the id column of address as both primary key and foreign key. Além disso, observe quethe @Id column of the Address entity no longer uses the @GeneratedValue annotation.

O atributomappedBy agora é movido para o sclassUser , uma vez que a chave estrangeira agora está presente no estábuloaddress .

5. Usando uma tabela de junção

Os mapeamentos um-para-um podem ser de dois tipos -OptionaleMandatory. So far, we’ve seen only mandatory relationships.

Agora, vamos imaginar que nossos funcionários sejam associados a uma estação de trabalho. É um para um, mas às vezes um funcionário pode não ter uma estação de trabalho e vice-versa.

5.1. Modelando com uma tabela de junção

As estratégias que discutimos until now force us to put null values in the column to handle optional relationships.

Normalmente, pensamos emmany-to-many relationships quando consideramos uma tabela de junção,but, using a join table, in this case, can help us to eliminate these null values:

An ER diagram relating Employees to Workstations via a Join Table

Agora, sempre que tivermos um relacionamento, faremos uma entrada naemp_workstation estável e evitaremos alto nulo.

5.2. Implementando com uma Tabela de Junção na JPA

Nosso primeiro exemplo usou@JoinColumn.  Desta vez, usaremos@JoinTable:

@Entity
@Table(name = "employee")
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne(cascade = CascadeType.ALL)
    @JoinTable(name = "emp_workstation",
      joinColumns =
        { @JoinColumn(name = "employee_id", referencedColumnName = "id") },
      inverseJoinColumns =
        { @JoinColumn(name = "workstation_id", referencedColumnName = "id") })
    private WorkStation workStation;

    //... getters and setters
}
@Entity
@Table(name = "workstation")
public class WorkStation {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //...

    @OneToOne(mappedBy = "workStation")
    private Employee employee;

    //... getters and setters
}

@JoinTable instrui o Hibernate a empregar a estratégia de junção da tabela enquanto mantém o relacionamento.

Além disso,Employee  é o proprietário deste relacionamento, pois escolhemos usar a anotação da tabela de junção nele.

6. Conclusão

Neste tutorial, aprendemos maneiras diferentes de manter uma associação individual no JPA e no Hibernate e quando usar cada uma.

O código-fonte deste tutorial pode ser encontradoover on GitHub.