java - stored - spring data jpa jpql
¿Cómo devolver un solo resultado de Spring-Data-JPA? (2)
Estoy tratando de obtener un solo resultado de una consulta de Spring Data. Estoy buscando devolver la identificación más grande de una tabla de usuario. Esperaba que fuera sencillo, pero estoy un poco perdido.
Hasta ahora, con base en esta publicación SO relacionada , he llegado a la conclusión de que necesito usar una Specification
para definir mi consulta y resultados de Page
d, especificando la cantidad de resultados que deseo recuperar. Lamentablemente, recibo una excepción de acceso a datos HibernateJdbcException
.
Mi Specification
/ Predicate
se supone que es bastante simple y refleja: from User order by id
:
Page<User> result =userRepository.findAll(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
query.orderBy(cb.desc(root.get("id")));
return query.getRestriction();
}
}, new PageRequest(0, 10));
MatcherAssert.assertThat(result.isFirstPage(), is(true));
User u = result.getContent().get(0);
Excepción lanzada:
org.springframework.orm.hibernate3.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL [n/a]; SQL state [90016]; error code [90016]; could not extract ResultSet; nested exception is org.hibernate.exception.GenericJDBCException: could not extract ResultSet
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:651)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:106)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.data.jpa.repository.support.LockModeRepositoryPostProcessor$LockModePopulatingMethodIntercceptor.invoke(LockModeRepositoryPostProcessor.java:92)
...
...
Caused by: org.hibernate.exception.GenericJDBCException: could not extract ResultSet
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:126)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:112)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:89)
at org.hibernate.loader.Loader.getResultSet(Loader.java:2065)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1862)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1838)
at org.hibernate.loader.Loader.doQuery(Loader.java:909)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:354)
at org.hibernate.loader.Loader.doList(Loader.java:2553)
at org.hibernate.loader.Loader.doList(Loader.java:2539)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2369)
at org.hibernate.loader.Loader.list(Loader.java:2364)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:496)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:387)
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:231)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1264)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:103)
at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:573)
at org.hibernate.jpa.internal.QueryImpl.getResultList(QueryImpl.java:449)
at org.hibernate.jpa.criteria.compile.CriteriaQueryTypeQueryAdapter.getResultList(CriteriaQueryTypeQueryAdapter.java:67)
at org.springframework.data.jpa.repository.query.QueryUtils.executeCountQuery(QueryUtils.java:406)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.readPage(SimpleJpaRepository.java:433)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:332)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:358)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155)
... 46 more
Caused by: org.h2.jdbc.JdbcSQLException: Column "USER1_.ID" must be in the GROUP BY list; SQL statement:
/* select count(generatedAlias0) from User as generatedAlias0, User as generatedAlias1 where 1=1 order by generatedAlias1.id desc */ select count(user0_.id) as col_0_0_ from user user0_ cross join user user1_ where 1=1 order by user1_.id desc [90016-173]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:331)
at org.h2.message.DbException.get(DbException.java:171)
at org.h2.message.DbException.get(DbException.java:148)
at org.h2.expression.ExpressionColumn.updateAggregate(ExpressionColumn.java:166)
at org.h2.command.dml.Select.queryGroup(Select.java:344)
at org.h2.command.dml.Select.queryWithoutCache(Select.java:620)
at org.h2.command.dml.Query.query(Query.java:314)
at org.h2.command.dml.Query.query(Query.java:284)
at org.h2.command.dml.Query.query(Query.java:36)
at org.h2.command.CommandContainer.query(CommandContainer.java:91)
at org.h2.command.Command.executeQuery(Command.java:195)
at org.h2.jdbc.JdbcPreparedStatement.executeQuery(JdbcPreparedStatement.java:106)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:80)
... 78 more
Estoy un poco perdido por el error de Hibernate, está pidiendo una cláusula group
. Supongo que tiene algo que ver con la forma en que he creado el predicado, pero no estoy seguro de cómo crear un predicado simple como este.
EDITAR
Tal como lo sugirió @OliverGierke, traté de eliminar la root = query.from(User.class)
pero hibernate aún arroja el mismo error (habilité el rastreo completo de consultas de hibernación). Extrañamente, sin embargo, esta vez, no hay GROUP BY
en el SQL generado, así que estoy aún más confundido que antes.
2014-03-18 11:59:44,475 [main] DEBUG org.hibernate.SQL -
/* select
count(generatedAlias0)
from
User as generatedAlias0
order by
generatedAlias0.id desc */ select
count(user0_.id) as col_0_0_
from
user user0_
order by
user0_.id desc
Hibernate:
/* select
count(generatedAlias0)
from
User as generatedAlias0
order by
generatedAlias0.id desc */ select
count(user0_.id) as col_0_0_
from
user user0_
order by
user0_.id desc
2014-03-18 11:59:44,513 [main] WARN hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 90016, SQLState: 90016
2014-03-18 11:59:44,513 [main] ERROR hibernate.engine.jdbc.spi.SqlExceptionHelper - Column "USER0_.ID" must be in the GROUP BY list; SQL statement:
/* select count(generatedAlias0) from User as generatedAlias0 order by generatedAlias0.id desc */ select count(user0_.id) as col_0_0_ from user user0_ order by user0_.id desc [90016-173]
No está utilizando la root
que se entrega en la instancia de Specification
para invocar el .get(…)
. Eso no permite que la instancia registre el id
está usando y, por lo tanto, lo agrega de forma transparente al conjunto de resultados.
Simplemente eliminando root = query.from(User.class);
debería hacer el truco.
Sin embargo, lo que me deja perplejo es que mencionas que tienes la intención de crear una consulta "buscar por ID". Lo que realmente está creando es un "buscar todo ordenado por id". Si es realmente lo primero que desea obtener, existe un findOne(…)
predefinido de findOne(…)
en CrudRepository
que puede usar.
Teniendo en cuenta los comentarios a continuación, parece que lo que realmente intenta lograr es encontrar un solo usuario con la identificación más pequeña. Esto también se puede lograr simplemente extendiendo PagingAndSortingRepository
y luego use un código de cliente como este:
interface UserRepository extends PagingAndSortingRepository<User, Long> { … }
Page<User> users = repository.findAll(new PageRequest(0, 1, Direction.ASC, "id"));
User user = users.getContent.get(0);
Esto limitará el resultado a la primera página por un tamaño de página de 1 con orden ascendente por id.
No estoy seguro de por qué está buscando una colección para obtener un solo resultado. Corrígeme si me equivoco, pero la solución a tu problema, tal como lo interpreté, es muy fácil de resolver usando @Query
. Agregue lo siguiente a su interfaz de repositorio.
@Query("SELECT max(t.id) FROM #{#entityName} t")
Integer getMaxId();