web-dev-qa-db-ja.com

Spring-Data-JPAから単一の結果を返す方法は?

Spring Dataクエリから単一の結果を取得しようとしています。ユーザーテーブルから最大のIDを返そうとしています。それが簡単だと思っていましたが、少し迷っています。

これまでのところ、 this related SO post に基づいて、Specificationを使用してクエリを定義する必要があるという結論に達しました。 Paged件の結果、取得する結果の数を指定します。残念ながら、HibernateJdbcExceptionデータアクセス例外が発生します。

私のSpecification/Predicateはかなり単純で、次のように反映されているはずです:_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);
_

スローされた例外:

_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
_

Hibernateエラーで少し迷っています-group句を要求しています。私はそれが私が述語を作成した方法と関係があると思いますが、このような単純な述語を作成する方法がわかりません。

[〜#〜]編集[〜#〜]

@OliverGierkeによって提案されたように、root = query.from(User.class)を削除しようとしましたが、hibernateは同じエラーをスローします(完全なhibernateクエリトレースを有効にしました)。奇妙なことに、今回は、生成されたSQLに_GROUP BY_がないため、以前よりもさらに混乱しています。

_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]
_
14
Eric B.

rootを使用してSpecificationインスタンスに渡して.get(…)メソッドを呼び出していない。これは、インスタンスが使用中のidを登録できないため、結果セットに透過的に追加します。

root = query.from(User.class);を削除するだけでうまくいくはずです。

しかし私を困惑させるのは、「idで検索」クエリを作成するつもりだとあなたが言っていることです。実際に作成しているのは、「IDですべての順序で検索」です。実際に取得したい前者の場合は、CrudRepositoryに事前定義されたfindOne(…)メソッドを使用できます。

以下のコメントを考えると、実際に達成しようとしているのは、最小のIDを持つ単一のユーザーを見つけることです。これは、単にPagingAndSortingRepositoryを拡張して、次のようなクライアントコードを使用することでも実現できます。

interface UserRepository extends PagingAndSortingRepository<User, Long> { … }

Page<User> users = repository.findAll(new PageRequest(0, 1, Direction.ASC, "id"));
User user = users.getContent.get(0);

これにより、結果が最初のページに制限され、ページサイズが1になり、IDの昇順で並べ替えられます。

12
Oliver Drotbohm

コレクションをフェッチして単一の結果を取得する理由がわかりません。私が間違っている場合は修正してください。ただし、私が解釈したように、問題の解決策は@Queryを使用して非常に簡単に解決できます。以下をリポジトリインターフェースに追加します。

@Query("SELECT max(t.id) FROM #{#entityName} t")
Integer getMaxId();
12
Bart