As melhores práticas para muitas associações com hibernado e JPA

Muitas-a-muitas associações são uma das associações mais comumente usadas com app e hibernado. Você pode encontrar muitos exemplos para eles no mundo real, e você pode mapeá – los com JPA e hibernar como uma associação uni-ou bidirecional em seu modelo de domínio.

mas você provavelmente também sabe que estes mapeamentos fornecem várias armadilhas. Neste artigo, vou mostrar-lhe 5 melhores práticas que irão ajudá-lo a evitar essas armadilhas e implementar mapeamentos eficientes. Vais aprender:

  1. O mais eficiente tipo de dados para a sua associação
  2. Por que você precisa de métodos de utilitário para gerenciar sua associação
  3. O direito FetchType para um eficiente mapeamento
  4. Quando e como usar o query específica de busca
  5. O CascadeType você deve evitar a todo custo

Eu não vou mergulhar em detalhes de um muitos-para-muitos de mapeamento. Se você não estiver exatamente certo de como criar tal mapeamento, por favor dê uma olhada na seção muitos-a-muitos no meu guia de mapeamento de associação.

O mais eficiente tipo de dados para a sua associação

YouTube

a Maioria dos desenvolvedores não gastar um monte de pensamentos sobre o tipo de dados de um para-muitos associação. Só escolhem um café.util.Listar porque é simples e não realiza quaisquer verificações para evitar duplicados.

não faz mal, se implementar uma classe Java básica ou se modelar uma associação de um para muitos/muitos para um. Mas você nunca deve usar uma lista se você modelar uma associação de muitos-para-muitos.

@Entitypublic class Book {// DON'T DO THIS!!!@ManyToMany@JoinTable(name = "book_author", joinColumns = { @JoinColumn(name = "fk_book") }, inverseJoinColumns = { @JoinColumn(name = "fk_author") })private List<Author> authors = new ArrayList<Author>();...}

Hibernate handes remove operations on Many-to-Many relationships that are mapped to a java.util.Lista muito ineficiente.

em = emf.createEntityManager();em.getTransaction().begin();// Get Book entity with 2 Authorsb = em.find(Book.class, 1L);// Remove one of the Authorb.getAuthors().remove(a);em.getTransaction().commit();em.close();

primeiro remove todos os registos da tabela de associação antes de inserir todos os restantes.

09:54:28,876 DEBUG - update Book set title=?, version=? where id=? and version=?09:54:28,878 DEBUG - delete from book_author where fk_book=?09:54:28,882 DEBUG - insert into book_author (fk_book, fk_author) values (?, ?)

em vez disso, deve modelar uma associação de muitos para muitos como um java.util.Definir.

@Entitypublic class Book {@ManyToMany@JoinTable(name = "book_author", joinColumns = { @JoinColumn(name = "fk_book") }, inverseJoinColumns = { @JoinColumn(name = "fk_author") })private Set<Author> authors = new HashSet<Author>();...}

hibernar em seguida, lida remover as operações na Associação muito melhor. Ele agora apenas remove os registros esperados da Associação e mantém os outros intocados.

10:00:37,709 DEBUG - update Book set title=?, version=? where id=? and version=?10:00:37,711 DEBUG - delete from book_author where fk_book=? and fk_author=?

Why you need utility methods to manage your association

bidirectional associations are mapped to an entity attribute on both ends of the relationships. Assim, no exemplo anterior, você tem um atributo de autores na entidade do livro, e um atributo de livros na entidade do autor. Isso torna a implementação de um JPQL ou CriteriaQuery muito confortável, porque você pode usar esses atributos para definir uma cláusula de adesão.

mas adicionar ou remover uma associação fica mais complicado. Você sempre precisa realizar a mudança em ambos os lados da Associação. Por exemplo, se você quiser adicionar um livro ao autor, você precisa adicioná-lo ao atributo livros da entidade autor, e você também precisa adicionar o autor o atributo autor na entidade livro. Caso contrário, o seu contexto de persistência actual contém dados inconsistentes que irá usar até ao final da sua transacção actual.

Book b = new Book();b.setTitle("Hibernate Tips - More than 70 solutions to common Hibernate problems");em.persist(b);Author a = em.find(Author.class, 1L);a.getBooks().add(b);b.getAuthors().add(a);

Utility methods on your Author and Book entities make updating and removing much easier. Dentro destes métodos, você realiza as operações necessárias em ambas as entidades.

@Entitypublic class Author {@ManyToMany(mappedBy = "authors")private Set<Book> books = new HashSet<Book>();...public void addBook(Book book) {this.books.add(book);book.getAuthors().add(this);}public void removeBook(Book book) {this.books.remove(book);book.getAuthors().remove(this);}}

O direito FetchType para um eficiente mapeamento

YouTube

Este é um rápido. Deve usar sempre o FetchType.Preguiçoso para as suas muitas associações. Ele diz ao seu fornecedor persistência para não obter as entidades associadas a partir do banco de dados até que você usá-los. Normalmente é o caso quando se chama o seu método getter pela primeira vez.

felizmente, esse é o padrão para todas as associações. Por isso, por favor, certifica-te que não o mudas.

E se quiser saber mais sobre os diferentes FetchTypes da JPA, por favor veja a minha introdução aos FetchTypes da JPA.

quando e como usar a obtenção específica da consulta

Se estiver a usar o FetchType.Preguiçoso, você precisa saber sobre busca específica. Caso contrário, a sua aplicação será muito lenta porque criou muitos problemas de selecção n+1.

YouTube

Quando você carrega uma entidade e uso de consulta específicos de busca, você dizer para o Hibernate que mapeada associações deve inicializar obtidos para cada entidade. Ele então estende a cláusula de seleção de sua consulta de modo que ele inclui as colunas mapeadas por essas outras entidades e inicializa as associações. E como as associações já estão inicializadas, o Hibernate não precisa realizar uma consulta adicional quando você acessar seu método getter pela primeira vez.

pode implementar a obtenção específica da consulta de várias formas diferentes. O mais simples é uma cláusula de JOIN FETCH, que vou mostrar aqui. Mas você também pode usar um @ NamedEntityGraph ou um EntityGraph, que eu expliquei em artigos anteriores.

A definição de uma cláusula de JOIN FETCH é quase idêntica a uma cláusula de JOIN simples numa consulta JPQL. Só precisa de adicionar a palavra-chave de busca.

Author a = em.createQuery("SELECT a FROM Author a JOIN FETCH a.books WHERE a.id = 1", Author.class).getSingleResult();

mesmo assim, uma junção e uma cláusula de JOIN FETCH são muito semelhantes, a cláusula JOIN FETCH tem um efeito muito maior na consulta SQL gerada. Ele não só é traduzido para uma junção SQL, como é o caso de uma cláusula de junção JPQL, ele também força o seu provedor persistência a estender a cláusula de seleção por todas as colunas que são mapeadas pela entidade associada.

o tipo em cascata deverá evitar a todo o custo

Se activar a cascata numa associação, o seu fornecedor de persistência aplica as operações que executa na entidade a todas as entidades associadas. Se ele faz isso para todas as operações ou apenas para algumas selecionadas depende do CascadeType configurado.

YouTube video

que pode soar como uma ideia incrível que torna a implementação da sua lógica de negócios muito mais fácil. E isso não é totalmente errado.

mas por favor evite os Cascadetipos remover e tudo, o que inclui Remover, para muitas associações. No melhor caso, ele só cria problemas de desempenho, mas no pior caso, ele também pode remover mais registros do que você pretendia.expliquei tanto as armadilhas como a sua solução em grandes detalhes num artigo anterior. Ou se você quiser mantê-lo simples, active a informação necessária programaticamente sobre as entidades associadas. Isso pode exigir mais algumas linhas de código, mas evita quaisquer efeitos colaterais inesperados.

conclusão

você pode encontrar muitos exemplos para muitas associações no mundo real, e você pode facilmente mapeá-los com JPA e hibernar. Infelizmente, estes mapeamentos simples escondem algumas armadilhas que você pode evitar seguindo estas 5 melhores práticas:

  1. associações de modelos como um java.util.Definir.
  2. fornece métodos de utilidade para adicionar ou remover uma entidade de uma associação.
  3. use sempre o FetchType.Preguiçoso, que é o padrão, para evitar problemas de desempenho.
  4. aplicar a obtenção específica da consulta para evitar problemas de selecção n+1.
  5. Não utilize os Cascadetipos remover e tudo.

Deixe uma resposta

O seu endereço de email não será publicado.