Eu tenho uma classe Person:
@Entity
public class Person {
@Id
@GeneratedValue
private Long id;
@ManyToMany(fetch = FetchType.LAZY)
private List<Role> roles;
// etc
}
Com uma relação muitos-para-muitos que é preguiçosa.
No meu controlador eu tenho
@Controller
@RequestMapping("/person")
public class PersonController {
@Autowired
PersonRepository personRepository;
@RequestMapping("/get")
public @ResponseBody Person getPerson() {
Person person = personRepository.findOne(1L);
return person;
}
}
E o PersonRepository é apenas esse código, escrito de acordo com este guia
public interface PersonRepository extends JpaRepository<Person, Long> {
}
No entanto, neste controlador, eu realmente preciso dos dados preguiçosos. Como posso acionar o carregamento?
Tentar acessá-lo falhará com
falhou ao inicializar preguiçosamente uma coleção de funções: no.dusken.momus.model.Person.roles, não foi possível inicializar o proxy - nenhuma Sessão
ou outras exceções, dependendo do que eu tente.
Minha descrição xml , caso necessário.
Obrigado.
Person
objeto, com algum parâmetro? NeleQuery
, inclua afetch
cláusula e carregueRoles
também para a pessoa.Respostas:
Você precisará fazer uma chamada explícita na coleção lenta para inicializá-la (a prática comum é chamar
.size()
para esse fim). No Hibernate, existe um método dedicado para isso (Hibernate.initialize()
), mas o JPA não tem equivalente. É claro que você precisará garantir que a chamada seja feita, quando a sessão ainda estiver disponível, então anote seu método do controlador@Transactional
. Uma alternativa é criar uma camada de Serviço intermediária entre o Controlador e o Repositório que possa expor métodos que inicializam coleções preguiçosas.Atualizar:
Observe que a solução acima é fácil, mas resulta em duas consultas distintas no banco de dados (uma para o usuário e outra para suas funções). Se você deseja obter melhor desempenho, inclua o seguinte método na interface do repositório do Spring Data JPA:
Esse método usará a cláusula de junção de busca do JPQL para carregar ansiosamente a associação de funções em uma única viagem de ida e volta ao banco de dados e, portanto, reduzirá a penalidade de desempenho incorrida pelas duas consultas distintas na solução acima.
fonte
join
sem ofetch
, o conjunto será retornado cominitialized = false
; portanto, ainda emitindo uma segunda consulta assim que o conjunto for acessado.fetch
é essencial para garantir que o relacionamento esteja completamente carregado e evitar a segunda consulta.Embora essa seja uma postagem antiga, considere usar @NamedEntityGraph (Javax Persistence) e @EntityGraph (Spring Data JPA). A combinação funciona.
Exemplo
e depois o repo da primavera como abaixo
fonte
@NamedEntityGraph
faz parte da API JPA 2.1, que não é implementada no Hibernate antes da versão 4.3.0.@EntityGraph(attributePaths = "employeeGroups")
pode ser usado diretamente em um Spring Data Repository para anotar um método sem a necessidade de um@NamedEntityGraph
no seu código @Entity - less, fácil de entender quando você abre o repositório.Você tem algumas opções
Mais trabalho, melhor desempenho.
Menos trabalho, geralmente aceitável em ambientes da web.
Menos trabalho, útil quando o OEMIV não está disponível, por exemplo, em um aplicativo Swing, mas também pode ser útil em implementações de repositório para inicializar qualquer entidade de uma só vez.
Para a última opção, escrevi uma classe de utilitário, JpaUtils para iniciar entidades em algum deph.
Por exemplo:
fonte
só pode ser carregado lentamente enquanto estiver dentro de uma transação. Assim, você pode acessar a coleção em seu repositório, que possui uma transação - ou o que eu normalmente faço é um
get with association
, ou definir fetchmode para ansioso.fonte
Acho que você precisa do OpenSessionInViewFilter para manter sua sessão aberta durante a renderização de exibição (mas não é uma boa prática).
fonte
Dados da Primavera
JpaRepository
Os dados do Spring
JpaRepository
definem os dois métodos a seguir:getOne
, que retorna um proxy de entidade adequado para definir uma associação@ManyToOne
ou@OneToOne
pai ao persistir em uma entidade filho .findById
, que retorna a entidade POJO após executar a instrução SELECT que carrega a entidade da tabela associadaNo entanto, no seu caso, você não chamar tanto
getOne
oufindById
:Então, suponho que o
findOne
método é um método que você definiu noPersonRepository
. No entanto, ofindOne
método não é muito útil no seu caso. Como você precisa buscar a coleçãoPerson
junto com o isroles
, é melhor usar umfindOneWithRoles
método.Métodos personalizados de dados de primavera
Você pode definir uma
PersonRepositoryCustom
interface, da seguinte maneira:E defina sua implementação assim:
É isso aí!
fonte
Você pode fazer o mesmo assim:
Basta usar faqQuestions.getFaqAnswers (). Size () em seu controlador e você obterá o tamanho da lista inicializada preguiçosamente, sem buscar a própria lista.
fonte