Qual é o "lado proprietário" em um mapeamento ORM?

128

O que exatamente significa o lado dono ? O que é uma explicação com alguns exemplos de mapeamento ( um para muitos, um para um, muitos para um )?

O texto a seguir é um trecho da descrição de @OneToOne na documentação do Java EE 6. Você pode ver o conceito que possui o lado nele.

Define uma associação de valor único para outra entidade que possui multiplicidade um para um. Normalmente, não é necessário especificar a entidade de destino associada explicitamente, pois ela geralmente pode ser inferida a partir do tipo de objeto que está sendo referenciado. Se o relacionamento for bidirecional, o lado não proprietário deverá usar o elemento mappedBy da anotação OneToOne para especificar o campo ou a propriedade do relacionamento do lado proprietário.

Apenas um aprendiz
fonte
5
Eu estava perdido até que li isso: javacodegeeks.com/2013/04/…
darga33 7/07
2
A tabela DB com a coluna chave estrangeira é tratada como lado proprietário. Portanto, a entidade comercial que representa essa tabela do banco de dados é o proprietário (lado proprietário) dessa relação. Não necessariamente, mas a maioria dos casos do lado Proprietário terá anotação @JoinColumn.
Diablo

Respostas:

202

Por que é necessária a noção de um lado dono:

A idéia de um lado proprietário de uma relação bidirecional deriva do fato de que nos bancos de dados relacionais não existem relações bidirecionais, como no caso de objetos. Nos bancos de dados, temos apenas relações unidirecionais - chaves estrangeiras.

Qual é o motivo do nome 'dono'?

O lado proprietário da relação rastreada pelo Hibernate é o lado da relação que possui a chave estrangeira no banco de dados.

Qual é o problema que a noção de possuir lado resolve?

Veja um exemplo de duas entidades mapeadas sem declarar um lado proprietário:

@Entity
@Table(name="PERSONS")
public class Person {
    @OneToMany
    private List<IdDocument>  idDocuments;
}

@Entity
@Table(name="ID_DOCUMENTS")
public class IdDocument {
    @ManyToOne
    private Person person;
}

Do ponto de vista do OO, esse mapeamento define não uma relação bidirecional, mas duas relações unidirecionais separadas.

O mapeamento criaria não apenas tabelas PERSONSe ID_DOCUMENTS, mas também criaria uma terceira tabela de associação PERSONS_ID_DOCUMENTS:

CREATE TABLE PERSONS_ID_DOCUMENTS
(
  persons_id bigint NOT NULL,
  id_documents_id bigint NOT NULL,
  CONSTRAINT fk_persons FOREIGN KEY (persons_id) REFERENCES persons (id),
  CONSTRAINT fk_docs FOREIGN KEY (id_documents_id) REFERENCES id_documents (id),
  CONSTRAINT pk UNIQUE (id_documents_id)
)

Observe a chave primária pkem ID_DOCUMENTSsomente. Nesse caso, o Hibernate controla os dois lados da relação independentemente: Se você adicionar um documento à relação Person.idDocuments, ele inserirá um registro na tabela de associação PERSON_ID_DOCUMENTS.

Por outro lado, se chamarmos idDocument.setPerson(person), alteramos a chave estrangeira person_id na tabela ID_DOCUMENTS. O Hibernate está criando duas relações unidirecionais (chave estrangeira) no banco de dados, para implementar uma relação bidirecional de objetos.

Como a noção de possuir lado resolve o problema:

Muitas vezes o que nós queremos é apenas uma chave estrangeira na tabela de ID_DOCUMENTSdireção PERSONSea mesa associação extra.

Para resolver isso, precisamos configurar o Hibernate para parar de rastrear as modificações na relação Person.idDocuments. O Hibernate deve rastrear apenas o outro lado da relação IdDocument.persone, para isso, adicionamos mappedBy :

@OneToMany(mappedBy="person")
private List<IdDocument>  idDocuments;

O que significa mappedBy?

Isso significa algo como: "as modificações deste lado da relação já estão mapeadas pelo outro lado da relação IdDocument.person, portanto, não é necessário rastreá-lo aqui separadamente em uma tabela extra".

Existem GOTCHAs, consequências?

Usando mappedBy , Se nós só chamar person.getDocuments().add(document), a chave estrangeira em ID_DOCUMENTSvão NÃO estar ligado ao novo documento, porque esta não é a / lateral proprietária rastreado da relação!

Para vincular o documento à nova pessoa, você precisa ligar explicitamente document.setPerson(person), porque esse é o lado proprietário da relação.

Ao usar mappedBy , é de responsabilidade do desenvolvedor saber qual é o lado proprietário e atualizar o lado correto da relação para acionar a persistência da nova relação no banco de dados.

Universidade Angular
fonte
17
A melhor resposta que eu acho que explica a Doutrina 'mappedBy' + 'inversedBy'.
Kurt Zhong
1
Obrigado por especificar os mapeamentos e a razão por trás do conceito.
Mohnish 13/04
1
Não sei se as coisas mudaram, mas no Hibernate 5.0.9.Final se eu "apenas ligar person.getDocuments().add(document)", o hibernate atualiza a chave estrangeira ID_DOCUMENTS.
K.Nicholas
1
@Karl Nicholas, oi, concordo com você, temos um atributo cascata na @OneToManyanotação que pode ser definida como PERSIST. Nesse caso, o hibernar salvará todas as entidades vinculadas ao banco de dados. Alguém pode esclarecer isso - por que o autor diz que o hibernate não rastreia alterações no lado não proprietário - mas, na verdade, o hibernado realiza o rastreamento?
Oleksandr Papchenko
3
cascade diz ao provedor para salvar as entidades filhas, mesmo que a entidade pai não as possua, portanto modifica efetivamente a regra. Se você tivesse (ou tiver) mappedBy = child.field e NÃO tivesse cascata, os filhos da lista não seriam salvos. Além disso, se você não mapeouBy AND não teve cascata, o pai é o proprietário do relacionamento e, se você colocar NOVOS filhos na lista e, em seguida, salvar o pai, será gerada uma exceção porque os novos IDs de filhos não estão disponíveis para ser salvo na tabela de junção. Espero que esclareça as coisas ... :)
K.Nicholas
142

Você pode imaginar que o lado proprietário é a entidade que tem a referência ao outro. No seu trecho, você tem um relacionamento individual. Como é uma relação simétrica , você terá que, se o objeto A estiver em relação ao objeto B, também o vice-versa for verdadeiro.

Isso significa que salvar no objeto A uma referência ao objeto B e salvar no objeto B uma referência ao objeto A será redundante: é por isso que você escolhe qual objeto "possui" o outro que tem a referência a ele.

Quando você tem um relacionamento um para muitos, os objetos relacionados à parte "muitos" serão o lado proprietário; caso contrário, você precisará armazenar muitas referências de um único objeto para uma multidão. Para evitar isso, todos os objetos da segunda classe terão um ponteiro para o único ao qual se referem (portanto, eles são o lado proprietário).

Para um relacionamento muitos-para-muitos, uma vez que você precisará de uma tabela de mapeamento separada de qualquer maneira, não haverá nenhum lado proprietário.

Em conclusão, o lado proprietário é a entidade que faz referência ao outro.

Jack
fonte
6
Obrigado pelo seu esclarecimento.
Apenas um aluno
2
ele pode ajudar a ver abaixo, bem a minha resposta por razões de 'mappedBy' os nomes e 'possuir lado', o que acontece se não definir um lado possuir, pegadinhas, espero que ajude
Universidade angular
5
Bem, eu acho que a resposta está correta. Mas para o Hibernate, pelo menos, até os relacionamentos muitos-para-muitos têm um lado proprietário. Isso tem implicações para o comportamento de atualização, por exemplo. Dê uma olhada na seção 4 ("Atualizar classe de modelo de hibernação") deste tutorial: viralpatel.net/blogs/…
Pfiver
11
Essa resposta confunde mais do que ajuda. De que adianta dizer "Você pode imaginar que o lado proprietário é a entidade que tem a referência ao outro", quando em relacionamentos bidirecionais, os dois objetos de Entidade terão uma referência um ao outro? Além disso, "Para um relacionamento muitos-para-muitos, já que você precisará de uma tabela de mapeamento separada de qualquer maneira, não haverá nenhum lado proprietário" é simplesmente incorreto: os @ManyToManyrelacionamentos também têm lados proprietários. Da mesma forma, os @OneToManyrelacionamentos podem usar tabelas de junção, e você ainda precisa especificar um lado proprietário.
Davids
5
Basicamente, esta é uma resposta bonitinha e agradável, que tem votos positivos porque é mais fácil de entender do que a verdade.
Davids