Skip to content

Commit

Permalink
Criteria pagination (#3132)
Browse files Browse the repository at this point in the history
  • Loading branch information
dstepanov authored Sep 17, 2024
1 parent 74fbe2a commit 792e8a0
Show file tree
Hide file tree
Showing 121 changed files with 2,490 additions and 575 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -290,7 +289,7 @@ public QueryResult buildSelect(@NonNull AnnotationMetadata annotationMetadata, @
buildWhereClause(annotationMetadata, predicate, queryState);
}

appendOrder(annotationMetadata, definition, queryState);
appendOrder(annotationMetadata, definition.order(), queryState);
appendForUpdate(QueryPosition.END_OF_QUERY, definition, queryState.getQuery());

return QueryResult.of(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,15 @@ public CriteriaBuilder getCriteriaBuilder() {
return sessionFactory.getCriteriaBuilder();
}

@Override
public boolean exists(CriteriaQuery<?> query) {
return executeRead(session -> {
try (Stream<?> stream = session.createQuery(query).stream()) {
return stream.findAny().isPresent();
}
});
}

@Override
public <R> R findOne(CriteriaQuery<R> query) {
return executeRead(session -> session.createQuery(query).uniqueResult());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,13 @@
import jakarta.persistence.criteria.CriteriaUpdate;
import org.hibernate.SessionFactory;
import org.hibernate.reactive.stage.Stage;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Collection;
import java.util.function.Function;
import java.util.stream.Stream;

/**
* Hibernate reactive implementation of {@link io.micronaut.data.operations.reactive.ReactiveRepositoryOperations}
Expand Down Expand Up @@ -430,6 +432,11 @@ public <R> Mono<R> findOne(CriteriaQuery<R> query) {
return withSession(session -> helper.monoFromCompletionStage(() -> session.createQuery(query).getSingleResult()));
}

@Override
public Publisher<Boolean> exists(CriteriaQuery<?> query) {
return withSession(session -> helper.monoFromCompletionStage(() -> session.createQuery(query).setMaxResults(1).getResultList().thenApply(l -> !l.isEmpty())));
}

@Override
public <T> Flux<T> findAll(CriteriaQuery<T> query) {
return withSession(session -> helper.monoFromCompletionStage(() -> session.createQuery(query).getResultList()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
* @param <T> The entity type
* @author Denis Stepanov
* @since 3.5.0
* @deprecated Replaced by {@link io.micronaut.data.repository.jpa.reactive.ReactiveStreamsJpaSpecificationExecutor}
*/
@Deprecated(forRemoval = true, since = "4.10")
public interface ReactiveStreamsJpaSpecificationExecutor<T> {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
* @param <T> The entity type
* @author Denis Stepanov
* @since 3.5.0
* @deprecated Replaced by {@link io.micronaut.data.repository.jpa.reactive.ReactiveStreamsJpaSpecificationExecutor}
*/
@Deprecated(forRemoval = true, since = "4.10")
public interface ReactorJpaSpecificationExecutor<T> extends ReactiveStreamsJpaSpecificationExecutor<T> {

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* Copyright 2017-2024 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.data.jdbc.mapper;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.convert.ConversionService;
import jakarta.persistence.Tuple;
import jakarta.persistence.TupleElement;

import java.util.List;
import java.util.Map;

/**
* An implementation of {@link Tuple}.
*
* @author Denis Stepanov
* @since 4.10
*/
@Internal
final class JdbcTuple implements Tuple {

private final ConversionService conversionService;
private final Object[] values;
private final Map<String, Integer> aliasToPosition;

public JdbcTuple(ConversionService conversionService, Object[] values, Map<String, Integer> aliasToPosition) {
this.conversionService = conversionService;
this.values = values;
this.aliasToPosition = aliasToPosition;
}

@Override
public <X> X get(TupleElement<X> tupleElement) {
throw new UnsupportedOperationException();
}

@Override
public <X> X get(String alias, Class<X> type) {
return conversionService.convertRequired(get(alias), type);
}

@Override
public Object get(String alias) {
return get(aliasToPosition.get(alias));
}

@Override
public <X> X get(int i, Class<X> type) {
return conversionService.convertRequired(get(i), type);
}

@Override
public Object get(int i) {
return values[i];
}

@Override
public Object[] toArray() {
return values;
}

@Override
public List<TupleElement<?>> getElements() {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright 2017-2024 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.data.jdbc.mapper;

import io.micronaut.core.annotation.Internal;
import io.micronaut.core.convert.ConversionService;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.data.exceptions.DataAccessException;
import io.micronaut.data.runtime.mapper.sql.SqlTypeMapper;
import jakarta.persistence.Tuple;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Map;

/**
* A mapper of {@link Tuple}.
*
* @author Denis Stepanov
* @since 4.10
*/
@Internal
public final class JdbcTupleMapper implements SqlTypeMapper<ResultSet, Tuple> {

private final ConversionService conversionService;

public JdbcTupleMapper(ConversionService conversionService) {
this.conversionService = conversionService;
}

@Override
public boolean hasNext(ResultSet resultSet) {
try {
return resultSet.next();
} catch (SQLException e) {
throw new DataAccessException("Failed to move the result set: " + e.getMessage(), e);
}
}

@Override
public Tuple map(ResultSet rs, Class<Tuple> type) throws DataAccessException {
try {
ResultSetMetaData metaData = rs.getMetaData();
Object[] values = new Object[metaData.getColumnCount()];
Map<String, Integer> aliasToPosition = CollectionUtils.newHashMap(values.length);
for (int i = 0; i < values.length; i++) {
values[i] = rs.getObject(i + 1);
String alias = metaData.getColumnName(i + 1);
aliasToPosition.put(alias, i);
}
return new JdbcTuple(conversionService, values, aliasToPosition);
} catch (SQLException e) {
throw new DataAccessException("Failed to read the result set: " + e.getMessage(), e);
}
}

@Override
public Object read(ResultSet object, String name) {
throw new UnsupportedOperationException();
}

@Override
public ConversionService getConversionService() {
return conversionService;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import io.micronaut.data.jdbc.mapper.ColumnNameExistenceAwareResultSetReader;
import io.micronaut.data.jdbc.mapper.ColumnNameResultSetReader;
import io.micronaut.data.jdbc.mapper.JdbcQueryStatement;
import io.micronaut.data.jdbc.mapper.JdbcTupleMapper;
import io.micronaut.data.jdbc.mapper.SqlResultConsumer;
import io.micronaut.data.jdbc.runtime.ConnectionCallback;
import io.micronaut.data.jdbc.runtime.PreparedStatementCallback;
Expand Down Expand Up @@ -96,6 +97,7 @@
import io.micronaut.transaction.TransactionOperations;
import jakarta.annotation.PreDestroy;
import jakarta.inject.Named;
import jakarta.persistence.Tuple;

import javax.sql.DataSource;
import java.sql.CallableStatement;
Expand Down Expand Up @@ -248,6 +250,11 @@ protected Integer getFirstResultSetIndex() {
return 1;
}

@Override
protected SqlTypeMapper<ResultSet, Tuple> createTupleMapper() {
return new JdbcTupleMapper(conversionService);
}

@Override
public <T> T persistOne(JdbcOperationContext ctx, T value, RuntimePersistentEntity<T> persistentEntity) {
SqlStoredQuery<T, ?> storedQuery = resolveEntityInsert(ctx.annotationMetadata, ctx.repositoryType, (Class<T>) value.getClass(), persistentEntity);
Expand Down Expand Up @@ -376,8 +383,8 @@ private <T, R> List<R> findAll(Connection connection, SqlPreparedQuery<T, R> pre
try (PreparedStatement ps = prepareStatement(connection::prepareStatement, preparedQuery, !applyPageable, false)) {
preparedQuery.bindParameters(new JdbcParameterBinder(connection, ps, preparedQuery));
return findAll(preparedQuery, ps);
} catch (SQLException e) {
throw new DataAccessException("Error executing SQL Query: " + e.getMessage(), e);
} catch (Throwable e) {
throw new DataAccessException("Error executing SQL Query: " + preparedQuery.getQuery() + " " + e.getMessage(), e);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.micronaut.data.jdbc.h2

import groovy.transform.Memoized
import io.micronaut.data.tck.repositories.BookAsyncRepository
import io.micronaut.data.tck.repositories.PersonAsyncRepository
import io.micronaut.data.tck.tests.AbstractAsyncRepositorySpec

Expand All @@ -27,4 +28,9 @@ class H2AsyncRepositorySpec extends AbstractAsyncRepositorySpec implements H2Tes
return context.getBean(H2AsyncPersonRepository)
}

@Memoized
@Override
BookAsyncRepository getBookRepository() {
return context.getBean(H2AsyncBookRepository)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package io.micronaut.data.jdbc.h2

import groovy.transform.Memoized
import io.micronaut.data.tck.repositories.BookReactiveRepository
import io.micronaut.data.tck.repositories.PersonReactiveRepository
import io.micronaut.data.tck.repositories.StudentReactiveRepository
import io.micronaut.data.tck.tests.AbstractReactiveRepositorySpec
Expand All @@ -34,4 +35,9 @@ class H2ReactiveRepositorySpec extends AbstractReactiveRepositorySpec implements
return context.getBean(H2StudentReactiveRepository)
}

@Memoized
@Override
BookReactiveRepository getBookRepository() {
return context.getBean(H2ReactiveBookRepository)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,7 @@
package io.micronaut.data.jdbc.h2

import groovy.transform.Memoized
import io.micronaut.data.tck.repositories.AuthorRepository
import io.micronaut.data.tck.repositories.BasicTypesRepository
import io.micronaut.data.tck.repositories.BookDtoRepository
import io.micronaut.data.tck.repositories.BookRepository
import io.micronaut.data.tck.repositories.CarRepository
import io.micronaut.data.tck.repositories.CityRepository
import io.micronaut.data.tck.repositories.CompanyRepository
import io.micronaut.data.tck.repositories.CountryRegionCityRepository
import io.micronaut.data.tck.repositories.CountryRepository
import io.micronaut.data.tck.repositories.EntityWithIdClass2Repository
import io.micronaut.data.tck.repositories.EntityWithIdClassRepository
import io.micronaut.data.tck.repositories.FaceRepository
import io.micronaut.data.tck.repositories.FoodRepository
import io.micronaut.data.tck.repositories.GenreRepository
import io.micronaut.data.tck.repositories.MealRepository
import io.micronaut.data.tck.repositories.NoseRepository
import io.micronaut.data.tck.repositories.PageRepository
import io.micronaut.data.tck.repositories.PersonRepository
import io.micronaut.data.tck.repositories.RegionRepository
import io.micronaut.data.tck.repositories.RoleRepository
import io.micronaut.data.tck.repositories.StudentRepository
import io.micronaut.data.tck.repositories.TimezoneBasicTypesRepository
import io.micronaut.data.tck.repositories.UserRepository
import io.micronaut.data.tck.repositories.UserRoleRepository
import io.micronaut.data.tck.repositories.*
import io.micronaut.data.tck.tests.AbstractRepositorySpec
import spock.lang.Shared

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
package io.micronaut.data.jdbc.sqlserver

import groovy.transform.Memoized
import io.micronaut.data.jdbc.postgres.PostgresEntityWithIdClass2Repository
import io.micronaut.data.jdbc.postgres.PostgresEntityWithIdClassRepository
import io.micronaut.data.tck.repositories.*
import io.micronaut.data.tck.tests.AbstractRepositorySpec

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2017-2024 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micronaut.data.jdbc.h2;

import io.micronaut.data.jdbc.annotation.JdbcRepository;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.tck.repositories.BookAsyncRepository;
import io.micronaut.data.tck.repositories.PersonAsyncRepository;

@JdbcRepository(dialect = Dialect.H2)
public interface H2AsyncBookRepository extends BookAsyncRepository {
}
Loading

0 comments on commit 792e8a0

Please sign in to comment.