java hibernate jpa jpql ejb-3.0

java - ¿Cómo puedo evitar la advertencia "firstResult/maxResults especificada con recopilación de recolección; aplicando en la memoria! ”cuando se usa Hibernate?



jpa jpql (5)

Aparece una advertencia en el registro del servidor "firstResult / maxResults especificado con la recuperación de la colección; ¡se aplica en la memoria!" . Sin embargo todo funciona bien. Pero no quiero esta advertencia.

Mi codigo es

public employee find(int id) { return (employee) getEntityManager().createQuery(QUERY).setParameter("id", id).getSingleResult(); }

Mi consulta es

QUERY = "from employee as emp left join fetch emp.salary left join fetch emp.department where emp.id = :id"


Aunque está obteniendo resultados válidos, la consulta SQL obtiene todos los datos y no es tan eficiente como debería.

Como expliqué en este artículo , puede activar fácilmente una consulta de JPQL que use tanto JOIN FETCH como la paginación:

List<Post> posts = entityManager.createQuery( "select p " + "from Post p " + "left join fetch p.comments " + "where p.title like :title " + "order by p.id", Post.class) .setParameter("title", titlePattern) .setMaxResults(maxResults) .getResultList();

en una consulta SQL que limita el resultado usando DENSE_RANK por el identificador principal:

List<Post> posts = entityManager.createNativeQuery( "select p_pc_r.* " + "from ( " + " select *, dense_rank() OVER (ORDER BY post_id) rank " + " from ( " + " select p.*, pc.* " + " from post p " + " left join post_comment pc on p.id = pc.post_id " + " where p.title like :title " + " order by p.id " + " ) p_pc " + ") p_pc_r " + "where p_pc_r.rank <= :rank", Post.class) .setParameter("title", titlePattern) .setParameter("rank", maxResults) .unwrap( NativeQuery.class ) .addEntity( "p", Post.class ) .addEntity( "pc", PostComment.class ) .setResultTransformer( DistinctPostResultTransformer.INSTANCE ) .getResultList();

Para transformar el conjunto de resultados tabulares en un gráfico de entidad, necesita un ResultTransformer que tiene el siguiente aspecto:

public class DistinctPostResultTransformer extends BasicTransformerAdapter { private static final DistinctPostResultTransformer INSTANCE = new DistinctPostResultTransformer(); @Override public List transformList(List list) { Map<Serializable, Identifiable> identifiableMap = new LinkedHashMap<>( list.size() ); for ( Object entityArray : list ) { if ( Object[].class.isAssignableFrom( entityArray.getClass() ) ) { Post post = null; PostComment comment = null; Object[] tuples = (Object[]) entityArray; for ( Object tuple : tuples ) { if(tuple instanceof Post) { post = (Post) tuple; } else if(tuple instanceof PostComment) { comment = (PostComment) tuple; } else { throw new UnsupportedOperationException( "Tuple " + tuple.getClass() + " is not supported!" ); } } Objects.requireNonNull(post); Objects.requireNonNull(comment); if ( !identifiableMap.containsKey( post.getId() ) ) { identifiableMap.put( post.getId(), post ); post.setComments( new ArrayList<>() ); } post.addComment( comment ); } } return new ArrayList<>( identifiableMap.values() ); } }

¡Eso es!


El motivo de esta advertencia es que cuando se usa la combinación de búsqueda, el orden en los conjuntos de resultados se define solo por el ID de la entidad seleccionada (y no por la búsqueda de unión).

Si esta clasificación en la memoria está causando problemas, no use firsResult / maxResults con JOIN FETCH.


El problema es que obtendrás producto cartesiano haciendo ÚNETE. La compensación cortará su conjunto de registros sin mirar si todavía está en la misma clase de identidad raíz


Para evitar esta ADVERTENCIA, debe cambiar la llamada getSingleResult a getResultList().get(0)


Supongo que el emp tiene muchos departamentos, que es una relación One to Many. Hibernate buscará muchas filas para esta consulta con los registros del departamento recuperados. Por lo tanto, el orden del conjunto de resultados no se puede decidir hasta que realmente haya obtenido los resultados en la memoria. Así que la paginación se hará en memoria.

Si no desea obtener los departamentos con emp, pero aún desea hacer alguna consulta basada en el departamento, puede lograr el resultado sin advertencia (sin hacer pedidos en la memoria). Para eso simplemente tienes que eliminar la cláusula "fetch". Así que algo como lo siguiente:

QUERY = "del empleado como emp dejó unirse emp.salary sal dejó unirse emp.department dep donde emp.id =: id y dep.name = ''testing'' y sal.salary> 5000"