Eu tenho uma relação entre três objetos de modelo em meu projeto (trechos de modelo e repositório no final do post.
Quando eu ligo, PlaceRepository.findById
ele dispara três consultas selecionadas:
("sql")
SELECT * FROM place p where id = arg
SELECT * FROM user u where u.id = place.user.id
SELECT * FROM city c LEFT OUTER JOIN state s on c.woj_id = s.id where c.id = place.city.id
Esse é um comportamento bastante incomum (para mim). Pelo que eu posso dizer depois de ler a documentação do Hibernate, ele sempre deve usar consultas JOIN. Não há diferença nas consultas quando FetchType.LAZY
alteradas para FetchType.EAGER
na Place
classe (consulta com SELECT adicional), o mesmo para a City
classe quando FetchType.LAZY
alterado para FetchType.EAGER
(consulta com JOIN).
Quando eu uso a CityRepository.findById
supressão de disparos, duas seleções:
SELECT * FROM city c where id = arg
SELECT * FROM state s where id = city.state.id
Meu objetivo é ter um comportamento igual em todas as situações (embora seja sempre JOIN ou SELECT, JOIN preferencial).
Definições de modelo:
Lugar, colocar:
@Entity
@Table(name = "place")
public class Place extends Identified {
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "id_user_author")
private User author;
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "area_city_id")
private City city;
//getters and setters
}
Cidade:
@Entity
@Table(name = "area_city")
public class City extends Identified {
@Fetch(FetchMode.JOIN)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "area_woj_id")
private State state;
//getters and setters
}
Repositórios:
PlaceRepository
public interface PlaceRepository extends JpaRepository<Place, Long>, PlaceRepositoryCustom {
Place findById(int id);
}
UserRepository:
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findAll();
User findById(int id);
}
CityRepository:
public interface CityRepository extends JpaRepository<City, Long>, CityRepositoryCustom {
City findById(int id);
}
Respostas:
Acho que Spring Data ignora o FetchMode. Eu sempre uso as anotações
@NamedEntityGraph
e@EntityGraph
ao trabalhar com Spring DataVerifique a documentação aqui
fonte
List<Place> findAll();
termina com a Exceçãoorg.springframework.data.mapping.PropertyReferenceException: No property find found for type Place!
. Funciona quando eu adiciono manualmente@Query("select p from Place p")
. Parece uma solução alternativa.@EntityGraph
é quase ununsable em cenários reais, uma vez que não pode ser especificado que tipo deFetch
nós queremos usar (JOIN
,SUBSELECT
,SELECT
,BATCH
). Isso em combinação com a@OneToMany
associação e faz com que o Hibernate busque toda a tabela para a memória, mesmo se usarmos a consultaMaxResults
.Em primeiro lugar,
@Fetch(FetchMode.JOIN)
e@ManyToOne(fetch = FetchType.LAZY)
são antagônicos, um instruindo um Fetch EAGER, enquanto o outro sugerindo um Fetch LAZY.A busca rápida raramente é uma boa escolha e, para um comportamento previsível, é melhor usar a
JOIN FETCH
diretiva de tempo de consulta :fonte
Spring-jpa cria a consulta usando o gerenciador de entidades, e o Hibernate irá ignorar o modo de busca se a consulta foi construída pelo gerenciador de entidades.
A seguir está a solução que usei:
Implementar um repositório personalizado que herda de SimpleJpaRepository
Substitua o método
getQuery(Specification<T> spec, Sort sort)
:No meio do método, adicione
applyFetchMode(root);
para aplicar o modo de busca, para fazer o Hibernate criar a consulta com a junção correta.(Infelizmente, precisamos copiar todo o método e métodos privados relacionados da classe base porque não havia outro ponto de extensão.)
Implementar
applyFetchMode
:fonte
"
FetchType.LAZY
" só será acionado para a tabela primária. Se em seu código você chamar qualquer outro método que tenha uma dependência de tabela pai, ele disparará uma consulta para obter as informações dessa tabela. (FIRES MULTIPLE SELECT)"
FetchType.EAGER
" criará junção de todas as tabelas, incluindo tabelas pai relevantes diretamente. (USOSJOIN
)Quando usar: Suponha que você precise obrigatoriamente usar o informartion da tabela pai dependente e escolha
FetchType.EAGER
. Se você só precisa de informações para determinados registros, useFetchType.LAZY
.Lembre-se,
FetchType.LAZY
precisa de uma fábrica de sessão de banco de dados ativa no local em seu código onde você escolher recuperar as informações da tabela pai.Por exemplo, para
LAZY
:Referência adicional
fonte
NamedEntityGraph
já que eu queria um gráfico de objeto não hidratado.O modo de busca só funcionará ao selecionar o objeto por id, ou seja, usando
entityManager.find()
. Como o Spring Data sempre criará uma consulta, a configuração do modo de busca não terá uso para você. Você pode usar consultas dedicadas com fetch joins ou usar gráficos de entidade.Quando você deseja o melhor desempenho, deve selecionar apenas o subconjunto dos dados de que realmente precisa. Para fazer isso, geralmente é recomendado usar uma abordagem DTO para evitar a busca de dados desnecessários, mas isso geralmente resulta em uma grande quantidade de código clichê sujeito a erros, já que você precisa definir uma consulta dedicada que constrói seu modelo DTO por meio de um JPQL expressão do construtor.
As projeções do Spring Data podem ajudar aqui, mas em algum ponto você precisará de uma solução como Blaze-Persistence Entity Views, que torna isso muito fácil e tem muito mais recursos em sua capa que serão úteis! Você acabou de criar uma interface DTO por entidade onde os getters representam o subconjunto de dados de que você precisa. Uma solução para o seu problema pode ser assim
Isenção de responsabilidade, sou o autor de Blaze-Persistence, então posso ser tendencioso.
fonte
I elaborado em dream83619 resposta para torná-lo lidar com Hibernate aninhados
@Fetch
anotações. Usei o método recursivo para encontrar anotações em classes associadas aninhadas.Portanto, você deve implementar o repositório customizado e o
getQuery(spec, domainClass, sort)
método de substituição . Infelizmente, você também deve copiar todos os métodos privados referenciados :(.Aqui está o código,
os métodos privados copiados são omitidos.EDIT: Adicionados métodos privados restantes.
fonte
http://jdpgrailsdev.github.io/blog/2014/09/09/spring_data_hibernate_join.html
neste link:
se você estiver usando JPA no topo do Hibernate, não há como definir o FetchMode usado pelo Hibernate para JOIN; no entanto, se você estiver usando JPA no topo do Hibernate, não há como definir o FetchMode usado pelo Hibernate para JOIN.
A biblioteca Spring Data JPA fornece uma API de especificações de design orientada por domínio que permite controlar o comportamento da consulta gerada.
fonte
De acordo com Vlad Mihalcea (consulte https://vladmihalcea.com/hibernate-facts-the-importance-of-fetch-strategy/ ):
Parece que a consulta JPQL pode substituir sua estratégia de busca declarada, então você terá que usar
join fetch
para carregar avidamente alguma entidade referenciada ou simplesmente carregar por id com EntityManager (que obedecerá sua estratégia de busca, mas pode não ser uma solução para seu caso de uso )fonte