Nesse caso, você usa a anotação JPA @JoinTable?

Respostas:

350

EDIT 2017-04-29 : Como apontado por alguns dos comentaristas, o JoinTableexemplo não precisa do mappedByatributo de anotação. De fato, as versões recentes do Hibernate se recusam a iniciar, imprimindo o seguinte erro:

org.hibernate.AnnotationException: 
   Associations marked as mappedBy must not define database mappings 
   like @JoinTable or @JoinColumn

Vamos fingir que você tem uma entidade chamada Projecte outra entidade chamadaTask e cada projeto pode ter muitas tarefas.

Você pode projetar o esquema do banco de dados para este cenário de duas maneiras.

A primeira solução é criar uma tabela denominada Projecte outra tabela denominada Taske adicionar uma coluna de chave estrangeira à tabela de tarefas denominada project_id:

Project      Task
-------      ----
id           id
name         name
             project_id

Dessa forma, será possível determinar o projeto para cada linha na tabela de tarefas. Se você usar essa abordagem, em suas classes de entidade, não precisará de uma tabela de junção:

@Entity
public class Project {

   @OneToMany(mappedBy = "project")
   private Collection<Task> tasks;

}

@Entity
public class Task {

   @ManyToOne
   private Project project;

}

A outra solução é usar uma terceira tabela, por exemplo Project_Tasks, e armazenar o relacionamento entre projetos e tarefas nessa tabela:

Project      Task      Project_Tasks
-------      ----      -------------
id           id        project_id
name         name      task_id

A Project_Taskstabela é chamada "Tabela de junção". Para implementar esta segunda solução no JPA, você precisa usar a @JoinTableanotação. Por exemplo, para implementar uma associação unidirecional um para muitos, podemos definir nossas entidades da seguinte forma:

Project entidade:

@Entity
public class Project {

    @Id
    @GeneratedValue
    private Long pid;

    private String name;

    @JoinTable
    @OneToMany
    private List<Task> tasks;

    public Long getPid() {
        return pid;
    }

    public void setPid(Long pid) {
        this.pid = pid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Task> getTasks() {
        return tasks;
    }

    public void setTasks(List<Task> tasks) {
        this.tasks = tasks;
    }
}

Task entidade:

@Entity
public class Task {

    @Id
    @GeneratedValue
    private Long tid;

    private String name;

    public Long getTid() {
        return tid;
    }

    public void setTid(Long tid) {
        this.tid = tid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

Isso criará a seguinte estrutura de banco de dados:

Diagrama ER 1

A @JoinTableanotação também permite personalizar vários aspectos da tabela de junção. Por exemplo, se tivéssemos anotado a taskspropriedade assim:

@JoinTable(
        name = "MY_JT",
        joinColumns = @JoinColumn(
                name = "PROJ_ID",
                referencedColumnName = "PID"
        ),
        inverseJoinColumns = @JoinColumn(
                name = "TASK_ID",
                referencedColumnName = "TID"
        )
)
@OneToMany
private List<Task> tasks;

O banco de dados resultante teria se tornado:

Diagrama ER 2

Por fim, se você deseja criar um esquema para uma associação muitos-para-muitos, o uso de uma tabela de junção é a única solução disponível.

Behrang Saeedzadeh
fonte
1
usando a primeira abordagem, tenho meu projeto preenchido com minhas tarefas e cada tarefa preenchida com o projeto pai antes da mesclagem e funciona, mas todas as minhas entradas são duplicadas com base no número de minhas tarefas. Um projeto com duas tarefas é salvo duas vezes no meu banco de dados. Por quê ?
Mai12
ATUALIZAÇÃO Não são duplica entradas no meu banco de dados, a hibernação está selecionando com a junção externa esquerda e eu não sei porque ..
MaikoID
2
Eu acredito que @JoinTable/@JoinColumnpode ser anotado no mesmo campo com mappedBy. Assim, o exemplo correcta deve ser manter o mappedByna Project, e mover o @JoinColumnpara Task.project (ou vice-versa)
Adrian Shum
2
Agradável! Mas eu tenho uma outra questão: se a unir a tabela Project_Tasksprecisa do namede Taskbem, que se torna três colunas: project_id, task_id, task_name, como conseguir isso?
macemers
5
Eu acho que você não deve ter mappedBy em seu segundo exemplo de uso para evitar este erroCaused by: org.hibernate.AnnotationException: Associations marked as mappedBy must not define database mappings like @JoinTable or @JoinColumn:
karthik m
14

Também é mais limpo de usar @JoinTable quando uma Entidade pode ser a criança em vários relacionamentos pai / filho com diferentes tipos de pais. Para acompanhar o exemplo de Behrang, imagine que uma Tarefa possa ser filha de Projeto, Pessoa, Departamento, Estudo e Processo.

A tasktabela deve ter 5 nullablecampos de chave estrangeira? Eu acho que não...

HDave
fonte
14

É a única solução para mapear uma associação ManyToMany: você precisa de uma tabela de junção entre as duas tabelas de entidades para mapear a associação.

Também é usado para associações OneToMany (geralmente unidirecionais) quando você não deseja adicionar uma chave estrangeira na tabela do lado muitos e, assim, mantê-lo independente do lado.

Pesquise @JoinTable na documentação do hibernate para obter explicações e exemplos.

JB Nizet
fonte
4

Ele permite que você lide com o relacionamento Muitos para Muitos. Exemplo:

Table 1: post

post has following columns
____________________
|  ID     |  DATE   |
|_________|_________|
|         |         |
|_________|_________|

Table 2: user

user has the following columns:

____________________
|     ID  |NAME     |
|_________|_________|
|         |         |
|_________|_________|

Ingressar na tabela permite criar um mapeamento usando:

@JoinTable(
  name="USER_POST",
  joinColumns=@JoinColumn(name="USER_ID", referencedColumnName="ID"),
  inverseJoinColumns=@JoinColumn(name="POST_ID", referencedColumnName="ID"))

irá criar uma tabela:

____________________
|  USER_ID| POST_ID |
|_________|_________|
|         |         |
|_________|_________|
slal
fonte
1
Pergunta: e se eu já tiver esta tabela adicional? A JoinTable não substituirá a existente, certo?
TheWandererr #
@TheWandererr, você encontrou a resposta para sua pergunta? Eu já tenho uma tabela de junção
asgs 27/10/17
No meu caso, está criando uma coluna redundante na tabela lateral proprietária. por exemplo. POST_ID no POST. Você pode sugerir por que isso está acontecendo?
SPS
0

@ManyToMany associações

Na maioria das vezes, você precisará usar a @JoinTableanotação para especificar o mapeamento de um relacionamento de tabela muitos para muitos:

  • o nome da tabela de links e
  • as duas colunas de chave estrangeira

Portanto, supondo que você tenha as seguintes tabelas de banco de dados:

Relacionamento de tabela de muitos para muitos

Na Postentidade, você mapeará esse relacionamento, assim:

@ManyToMany(cascade = {
    CascadeType.PERSIST,
    CascadeType.MERGE
})
@JoinTable(
    name = "post_tag",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private List<Tag> tags = new ArrayList<>();

A @JoinTableanotação é usada para especificar o nome da tabela por meio do nameatributo, bem como a coluna Chave Externa que faz referência à posttabela (por exemplo, joinColumns) e a coluna Chave Estrangeira na post_tagtabela de links que faz referência à Tagentidade por meio do inverseJoinColumnsatributo.

Observe que o atributo em cascata da @ManyToManyanotação está definido como PERSISTe MERGEsomente porque a cascata REMOVEé uma má ideia, já que a instrução DELETE será emitida para o outro registro pai, tagno nosso caso, não para o post_tagregistro. Para mais detalhes sobre este tópico, consulte este artigo .

Unidirecional @OneToManyAssociações

As @OneToManyassociações unidirecionais , que carecem de@JoinColumn mapeamento, se comportam como relacionamentos de tabela muitos para muitos, em vez de um para muitos.

Portanto, supondo que você tenha os seguintes mapeamentos de entidade:

@Entity(name = "Post")
@Table(name = "post")
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @OneToMany(
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<PostComment> comments = new ArrayList<>();

    //Constructors, getters and setters removed for brevity
}

@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {

    @Id
    @GeneratedValue
    private Long id;

    private String review;

    //Constructors, getters and setters removed for brevity
}

O Hibernate assumirá o seguinte esquema de banco de dados para o mapeamento de entidade acima:

Tabelas unidirecionais do banco de dados de associação <code> @OneToMany </code> JPA

Como já explicado, o @OneToManymapeamento JPA unidirecional se comporta como uma associação muitos-para-muitos.

Para personalizar a tabela de links, você também pode usar a @JoinTableanotação:

@OneToMany(
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
@JoinTable(
    name = "post_comment_ref",
    joinColumns = @JoinColumn(name = "post_id"),
    inverseJoinColumns = @JoinColumn(name = "post_comment_id")
)
private List<PostComment> comments = new ArrayList<>();

E agora, a tabela de links será chamada post_comment_refe as colunas Foreign Key serão post_id, para a posttabela e post_comment_idpara a post_commenttabela.

@OneToManyAssociações unidirecionais não são eficientes; portanto, é melhor usar @OneToManyassociações bidirecionais ou apenas o @ManyToOnelado. Confira este artigo para obter mais detalhes sobre este tópico.

Vlad Mihalcea
fonte