Pergunta

Eu tenho uma pergunta sobre os relacionamentos JPA-2.0 (provedor é hibernado) e sua administração correspondente em Java. Vamos supor que eu tenha um departamento e uma entidade dos funcionários:

@Entity
public class Department {
  ...
  @OneToMany(mappedBy = "department")
  private Set<Employee> employees = new HashSet<Employee>();
  ...
}

@Entity
public class Employee {
  ...
  @ManyToOne(targetEntity = Department.class)
  @JoinColumn
  private Department department;
  ...
}

Agora eu sei que tenho que gerenciar os relacionamentos de Java, como no teste de unidade a seguir:

@Transactional
@Test
public void testBoth() {
  Department d = new Department();
  Employee e = new Employee();
  e.setDepartment(d);
  d.getEmployees().add(e);
  em.persist(d);
  em.persist(e);
  assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
  assertNotNull(em.find(Department.class, d.getId()).getEmployees());
}

Se eu deixar de fora também e.setDepartment(d) ou d.getEmployees().add(e) As afirmações falharão. Até agora tudo bem. E se eu comprometer a transação do banco de dados no meio?

@Test
public void testBoth() {
  EntityManager em = emf.createEntityManager();
  em.getTransaction().begin();
  Department d = new Department();
  Employee e = new Employee();
  e.setDepartment(d);
  d.getEmployees().add(e);
  em.persist(d);
  em.persist(e);
  em.getTransaction().commit();
  em.close();
  em = emf.createEntityManager();
  em.getTransaction().begin();
  assertNotNull(em.find(Employee.class, e.getId()).getDepartment());
  assertNotNull(em.find(Department.class, d.getId()).getEmployees());
  em.getTransaction().commit();
  em.close();
}

Ainda preciso gerenciar os dois lados da relação? Não, como se vê, não preciso. Com esta modificação

e.setDepartment(d);
//d.getEmployees().add(e);

As afirmações ainda são bem -sucedidas. No entanto, se eu só definir o outro lado:

//e.setDepartment(d);
d.getEmployees().add(e);

as afirmações falham. Por quê? É porque o funcionário é o lado proprietário da relação? Posso mudar esse comportamento anotando de maneira diferente? Ou é sempre o lado "One" do "Onetomany" que determina quando o campo de chave estrangeira no banco de dados é preenchido?

Foi útil?

Solução

As relações de entidade no JPA têm lados próprios e inversos. As atualizações do banco de dados são determinadas pelo estado do lado próprio. No seu caso Employee é um lado próprio devido ao mappedBy atributo.

De Especificação JPA 2.0:

2.9 Relacionamentos de entidade

...

Os relacionamentos podem ser bidirecionais ou unidirecionais. Um relacionamento bidirecional tem um lado próprio e um lado inverso (não proprietário). Um relacionamento unidirecional tem apenas um lado proprietário. O lado próprio de um relacionamento determina as atualizações do relacionamento no banco de dados, conforme descrito na Seção 3.2.4.

As seguintes regras se aplicam a relacionamentos bidirecionais:

  • O lado inverso de um relacionamento bidirecional deve se referir ao seu próprio lado, usando o elemento mapeado do Onetoone, Onetomany ou Manotatomany Anotation. O elemento Mappedby designa a propriedade ou campo na entidade que é o proprietário do relacionamento.
  • Os muitos lados das relações bidirecionais um para muitos / muitos para um devem ser o lado proprietário, portanto, o elemento mapeado não pode ser especificado na anotação de muitos pontos.
  • Para relações bidirecionais individuais, o lado próprio corresponde ao lado que contém a chave estrangeira correspondente.
  • Para muitos relacionamentos bidirecionais muitos para muitos, ambos os lados podem ser o lado proprietário.

Outras dicas

Não sei o que seu teste está tentando demonstrar, mas o fato é que você devo Lidar com os dois lados da associação ao trabalhar com associações bidirecionais. Não fazer isso está incorreto. Período.

Atualizar: Embora a referência da especificação mencionada pelo AxTAVT seja, obviamente, precisa, insisto, você definitivamente deve definir os dois lados de uma associação bidirecional. Não fazer isso está incorreto e a associação entre suas entidades no contexto da primeira persistência é quebrado. o Livro da Wiki JPA coloca assim:

Como em todos os relacionamentos bidirecionais, é a responsabilidade do seu modelo de objeto e aplicação manter o relacionamento em ambas as direção. Não há mágica no JPA, se você adicionar ou remover um lado da coleção, você também deve adicionar ou remover do outro lado, veja corrupção do objeto. Tecnicamente, o banco de dados será atualizado corretamente se você adicionar/remover apenas do lado próprio do relacionamento, mas seu modelo de objeto ficará fora de sincronia, o que pode causar problemas.

Em outras palavras, o único correto e seguro A maneira de gerenciar sua associação bidirecional em Java é definir os dois lados do link. Isso geralmente é feito usando métodos de gerenciamento de links defensivos, como este:

@Entity
public class Department {
    ...
    @OneToMany(mappedBy = "department")
    private Set<Employee> employees = new HashSet<Employee>();
    ...

    public void addToEmployees(Employee employee) {
        this.employees.add(employee);
        employee.setDepartment(this);
    }
}

Repito, não fazê -lo está incorreto. Seu teste só funciona porque você está atingindo o banco de dados em um novo contexto de persistência (ou seja, uma situação muito particular, não a geral), mas o código quebraria em muitas outras situações.

A razão pela qual o segundo teste em um novo contexto de persistência é bem -sucedido se você atualizar apenas o lado próprio em um contexto anterior é que o provedor de persistência obviamente não pode saber que, ao persistir, você também não atualizou o lado inverso. Ele só se importa com o lado proprietário para fins de persistência. No entanto, quando você obtém objetos persistentes de um provedor de persistência, o provedor define as associações bidirecionais adequadamente de ambos os lados (é simplesmente assumido que eles também foram persistidos adequadamente). No entanto, como muitos outros aqui já apontaram, não é de responsabilidade do provedor de persistência concluir associações bidirecionais recém -criadas e você sempre deve manter adequadamente as associações bidirecionais em seu código.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top