starter query mkyong example data java spring jpa spring-data spring-data-jpa

java - query - Spring Data JPA+JpaSpecificationExecutor+EntityGraph



spring-boot-starter-data-jpa (4)

(Usando Spring Data JPA) Tengo dos entidades Parent e Child con una relación bidireccional OneToMany / ManyToOne entre ellos. Agrego un @NamedEntityGraph a la entidad principal así:

@Entity @NamedEntityGraph(name = "Parent.Offspring", attributeNodes = @NamedAttributeNodes("children")) public class Parent{ //blah blah blah @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY) Set<Child> children; //blah blah blah }

Observe que el tipo de búsqueda para los hijos de los padres es LAZY. Esto es a propósito. No siempre quiero cargar con entusiasmo a los niños cuando estoy consultando a un padre individual. Normalmente podría usar mi gráfico de entidad con nombre para cargar ansiosamente a los niños a pedido, por así decirlo. Pero.....

Hay una situación específica en la que me gustaría consultar por uno o más padres y con muchas ganas de cargar a sus hijos. Además de esto, necesito poder construir esta consulta programáticamente. Spring Data proporciona el JpaSpecificationExecutor que nos permite crear consultas dinámicas, pero no puedo averiguar cómo usarlo junto con los gráficos de entidades para los niños ansiosos de cargar en este caso específico. ¿Es esto posible? ¿Hay alguna otra forma de cargar con entusiasmo "a muchas entidades utilizando especificaciones?


La respuesta de Joepie está bien.

pero no es necesario crear repositoryFactoryBeanClass, configurar repositoryBaseClass

@EnableJpaRepositories( basePackages = {"your.package"}, repositoryBaseClass = CustomRepositoryImpl.class)


La solución es crear una interfaz de repositorio personalizada que implemente estas características:

@NoRepositoryBean public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> { List<T> findAll(Specification<T> spec, EntityGraphType entityGraphType, String entityGraphName); Page<T> findAll(Specification<T> spec, Pageable pageable, EntityGraphType entityGraphType, String entityGraphName); List<T> findAll(Specification<T> spec, Sort sort, EntityGraphType entityGraphType, String entityGraphName); T findOne(Specification<T> spec, EntityGraphType entityGraphType, String entityGraphName); }

También crea una implementación:

@NoRepositoryBean public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CustomRepository<T, ID> { private EntityManager em; public CustomRepositoryImpl(Class<T> domainClass, EntityManager em) { super(domainClass, em); this.em = em; } @Override public List<T> findAll(Specification<T> spec, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery<T> query = getQuery(spec, (Sort) null); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return query.getResultList(); } @Override public Page<T> findAll(Specification<T> spec, Pageable pageable, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery<T> query = getQuery(spec, pageable.getSort()); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return readPage(query, pageable, spec); } @Override public List<T> findAll(Specification<T> spec, Sort sort, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery<T> query = getQuery(spec, sort); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return query.getResultList(); } @Override public T findOne(Specification<T> spec, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery<T> query = getQuery(spec, (Sort) null); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return query.getSingleResult(); } }

Y crea una fábrica:

public class CustomRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> { protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new CustomRepositoryFactory(entityManager); } private static class CustomRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory { private EntityManager entityManager; public CustomRepositoryFactory(EntityManager entityManager) { super(entityManager); this.entityManager = entityManager; } protected Object getTargetRepository(RepositoryMetadata metadata) { return new CustomRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), entityManager); } protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { // The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory //to check for QueryDslJpaRepository''s which is out of scope. return CustomRepository.class; } } }

Y cambie el bean de fábrica del repositorio predeterminado al nuevo bean, por ejemplo, en el arranque de primavera agregue esto a la configuración:

@EnableJpaRepositories( basePackages = {"your.package"}, repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class )

Para obtener más información sobre los repositorios personalizados: http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-behaviour-for-all-repositories


Para complementar las respuestas de Joep y , debo decir que con las nuevas versiones de Spring Data JPA, tendrá que modificar el constructor de CustomRepositoryImpl . Ahora la documentation dice:

La clase debe tener un constructor de la superclase que está utilizando la implementación de la fábrica de repositorios específica de la tienda. En caso de que la clase base del repositorio tenga varios constructores, anule el que tome una EntityInformation más un objeto de infraestructura específico de la tienda (por ejemplo, un EntityManager o una clase de plantilla).

Yo uso el siguiente constructor:

public CustomRepositoryImpl(JpaEntityInformation<T,?> entityInformation, EntityManager em) { super(entityInformation, em); this.domainClass = entityInformation.getJavaType(); this.em = em; }

También he añadido un campo privado para almacenar la clase de dominio:

private final Class<T> domainClass;

Esto me permite deshacerme del método obsoleto readPage(javax.persistence.TypedQuery<T> query, Pageable pageable, @Nullable Specification<T> spec) y usar en su lugar:

@Override public Page<T> findAll(Specification<T> spec, Pageable pageable, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery<T> query = getQuery(spec, pageable.getSort()); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return readPage(query, domainClass, pageable, spec); }


Project Spring Data JPA EntityGraph implementa algunos de los enfoques mencionados en las otras respuestas.

Tiene, por ejemplo, estas interfaces de repositorio adicionales:

  • EntityGraphJpaRepository que es equivalente a JpaRepository estándar
  • EntityGraphJpaSpecificationExecutor que es equivalente al estándar JpaSpecificationExecutor

Consulte la documentación de referencia para ver algunos ejemplos.