J'essaie d'obtenir un seul résultat à partir d'une requête Spring Data. Je cherche à renvoyer le plus grand ID d'une table utilisateur. J'espérais que ce serait simple, mais je suis un peu perdu.
Jusqu'à présent, sur la base de ceci SO post , je suis arrivé à la conclusion que je dois utiliser un Specification
pour définir ma requête et Page
d résultats, spécifiant le nombre de résultats que je veux récupérer. Malheureusement, je reçois une exception d'accès aux données HibernateJdbcException
.
Mon Specification
/Predicate
est censé être assez simple et refléter: 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);
Exception levée:
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
Je suis un peu perdu par l'erreur Hibernate - il demande une clause group
. Je suppose que cela a quelque chose à voir avec la façon dont j'ai créé le prédicat, mais je ne sais pas comment créer un prédicat simple comme celui-ci.
[~ # ~] modifier [~ # ~]
Comme suggéré par @OliverGierke, j'ai essayé de supprimer la root = query.from(User.class)
mais la mise en veille prolongée génère toujours la même erreur (j'ai activé le suivi complet des requêtes de mise en veille prolongée). Étrangement, cependant, cette fois, il n'y a pas de GROUP BY
Dans le SQL généré, donc je suis encore plus confus qu'auparavant.
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]
Vous n'utilisez pas la fonction root
qui est transférée dans l'instance Specification
pour invoquer la méthode .get(…)
. Cela ne permet pas à l'instance d'enregistrer id
en cours d'utilisation et donc de l'ajouter de manière transparente au jeu de résultats.
La simple suppression de root = query.from(User.class);
devrait suffire.
Ce qui m'intrigue cependant, c'est que vous mentionnez que vous avez l'intention de créer une requête "find by id". Ce que vous êtes en train de construire est une "recherche de tous classés par identifiant". Si c'est vraiment le premier que vous souhaitez obtenir, il existe une méthode findOne(…)
prédéfinie sur CrudRepository
que vous pouvez utiliser.
Compte tenu des commentaires ci-dessous, il semble que ce que vous essayez réellement de réaliser soit de trouver un seul utilisateur avec le plus petit identifiant. Cela peut également être réalisé en étendant simplement PagingAndSortingRepository
puis en utilisant le code client comme ceci:
interface UserRepository extends PagingAndSortingRepository<User, Long> { … }
Page<User> users = repository.findAll(new PageRequest(0, 1, Direction.ASC, "id"));
User user = users.getContent.get(0);
Cela limitera le résultat à la première page par une taille de page de 1 avec un ordre croissant par id.
Je ne sais pas pourquoi vous récupérez une collection pour obtenir un seul résultat. Corrigez-moi si je me trompe, mais la solution à votre problème, telle que je l'ai interprétée, est très facile à résoudre en utilisant @Query
. Ajoutez ce qui suit à votre interface de référentiel.
@Query("SELECT max(t.id) FROM #{#entityName} t")
Integer getMaxId();