All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.data.redis.repository.support.QueryByExampleRedisExecutor Maven / Gradle / Ivy

There is a newer version: 3.2.5
Show newest version
/*
 * Copyright 2018-2022 the original author or 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 org.springframework.data.redis.repository.support;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
import org.springframework.data.mapping.model.EntityInstantiators;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.redis.core.RedisKeyValueTemplate;
import org.springframework.data.redis.core.convert.IndexResolver;
import org.springframework.data.redis.core.convert.PathIndexResolver;
import org.springframework.data.redis.repository.query.ExampleQueryMapper;
import org.springframework.data.redis.repository.query.RedisOperationChain;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.data.repository.query.FluentQuery;
import org.springframework.data.repository.query.QueryByExampleExecutor;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.data.util.Streamable;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * Repository fragment implementing Redis {@link QueryByExampleExecutor Query-by-Example} operations.
 * 

* This executor uses {@link ExampleQueryMapper} to map {@link Example}s into {@link KeyValueQuery} to execute its query * methods. * * @author Mark Paluch * @author Christoph Strobl * @since 2.1 */ @SuppressWarnings("unchecked") public class QueryByExampleRedisExecutor implements QueryByExampleExecutor, BeanFactoryAware, BeanClassLoaderAware { private final EntityInformation entityInformation; private final RedisKeyValueTemplate keyValueTemplate; private final ExampleQueryMapper mapper; private final SpelAwareProxyProjectionFactory projectionFactory; private final EntityInstantiators entityInstantiators = new EntityInstantiators(); /** * Create a new {@link QueryByExampleRedisExecutor} given {@link EntityInformation} and {@link RedisKeyValueTemplate}. * This constructor uses the configured {@link IndexResolver} from the converter. * * @param entityInformation must not be {@literal null}. * @param keyValueTemplate must not be {@literal null}. */ public QueryByExampleRedisExecutor(EntityInformation entityInformation, RedisKeyValueTemplate keyValueTemplate) { this(entityInformation, keyValueTemplate, keyValueTemplate.getConverter().getIndexResolver() != null ? keyValueTemplate.getConverter().getIndexResolver() : new PathIndexResolver(keyValueTemplate.getMappingContext())); } /** * Create a new {@link QueryByExampleRedisExecutor} given {@link EntityInformation} and {@link RedisKeyValueTemplate}. * * @param entityInformation must not be {@literal null}. * @param keyValueTemplate must not be {@literal null}. */ public QueryByExampleRedisExecutor(EntityInformation entityInformation, RedisKeyValueTemplate keyValueTemplate, IndexResolver indexResolver) { Assert.notNull(entityInformation, "EntityInformation must not be null!"); Assert.notNull(keyValueTemplate, "RedisKeyValueTemplate must not be null!"); Assert.notNull(indexResolver, "IndexResolver must not be null!"); this.entityInformation = entityInformation; this.keyValueTemplate = keyValueTemplate; this.mapper = new ExampleQueryMapper(keyValueTemplate.getMappingContext(), indexResolver); this.projectionFactory = new SpelAwareProxyProjectionFactory(); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.projectionFactory.setBeanFactory(beanFactory); } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.projectionFactory.setBeanClassLoader(classLoader); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findOne(org.springframework.data.domain.Example) */ @Override public Optional findOne(Example example) { return Optional.ofNullable(doFindOne(example)); } @Nullable private S doFindOne(Example example) { Iterator iterator = doFind(example); if (iterator.hasNext()) { S result = iterator.next(); if (iterator.hasNext()) { throw new IncorrectResultSizeDataAccessException(1); } return result; } return null; } private Iterator doFind(Example example) { RedisOperationChain operationChain = createQuery(example); KeyValueQuery query = new KeyValueQuery<>(operationChain); return (Iterator) keyValueTemplate.find(query.limit(2), entityInformation.getJavaType()).iterator(); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example) */ @Override public Iterable findAll(Example example) { RedisOperationChain operationChain = createQuery(example); return (Iterable) keyValueTemplate.find(new KeyValueQuery<>(operationChain), entityInformation.getJavaType()); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort) */ @Override public Iterable findAll(Example example, Sort sort) { throw new UnsupportedOperationException("Ordering is not supported"); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Pageable) */ @Override public Page findAll(Example example, Pageable pageable) { Assert.notNull(pageable, "Pageable must not be null!"); RedisOperationChain operationChain = createQuery(example); KeyValueQuery query = new KeyValueQuery<>(operationChain); List result = (List) keyValueTemplate.find( query.orderBy(pageable.getSort()).skip(pageable.getOffset()).limit(pageable.getPageSize()), entityInformation.getJavaType()); return PageableExecutionUtils.getPage(result, pageable, () -> operationChain.isEmpty() ? keyValueTemplate.count(entityInformation.getJavaType()) : keyValueTemplate.count(query, entityInformation.getJavaType())); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#count(org.springframework.data.domain.Example) */ @Override public long count(Example example) { RedisOperationChain operationChain = createQuery(example); return keyValueTemplate.count(new KeyValueQuery<>(operationChain), entityInformation.getJavaType()); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#exists(org.springframework.data.domain.Example) */ @Override public boolean exists(Example example) { return count(example) > 0; } /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryByExampleExecutor#findBy(org.springframework.data.domain.Example, java.util.function.Function) */ @Override public R findBy(Example example, Function, R> queryFunction) { Assert.notNull(example, "Example must not be null!"); Assert.notNull(queryFunction, "Query function must not be null!"); return queryFunction.apply(new FluentQueryByExample<>(example, example.getProbeType())); } private RedisOperationChain createQuery(Example example) { Assert.notNull(example, "Example must not be null!"); return mapper.getMappedExample(example); } /** * {@link org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery} using {@link Example}. * * @author Mark Paluch * @since 2.6 */ class FluentQueryByExample implements FluentQuery.FetchableFluentQuery { private final Example example; private final Sort sort; private final Class domainType; private final Class resultType; FluentQueryByExample(Example example, Class resultType) { this(example, Sort.unsorted(), resultType, resultType); } FluentQueryByExample(Example example, Sort sort, Class domainType, Class resultType) { this.example = example; this.sort = sort; this.domainType = domainType; this.resultType = resultType; } @Override public FetchableFluentQuery sortBy(Sort sort) { return new FluentQueryByExample<>(example, sort, domainType, resultType); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class) */ @Override public FetchableFluentQuery as(Class resultType) { return new FluentQueryByExample<>(example, sort, domainType, resultType); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection) */ @Override public FetchableFluentQuery project(Collection properties) { return this; } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue() */ @Nullable @Override public R oneValue() { S one = doFindOne(example); if (one != null) { return getConversionFunction(entityInformation.getJavaType(), resultType).apply(one); } return null; } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue() */ @Nullable @Override public R firstValue() { Iterator iterator = doFind(example); if (iterator.hasNext()) { return getConversionFunction(entityInformation.getJavaType(), resultType).apply(iterator.next()); } return null; } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all() */ @Override public List all() { return stream().collect(Collectors.toList()); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable) */ @Override public Page page(Pageable pageable) { Assert.notNull(pageable, "Pageable must not be null!"); Function conversionFunction = getConversionFunction(entityInformation.getJavaType(), resultType); List content = findAll(example, pageable).stream().map(conversionFunction).collect(Collectors.toList()); return PageableExecutionUtils.getPage(content, pageable, this::count); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream() */ @Override public Stream stream() { Function conversionFunction = getConversionFunction(entityInformation.getJavaType(), resultType); if (sort.isSorted()) { return findAll(example, PageRequest.of(0, Integer.MAX_VALUE, sort)).stream().map(conversionFunction); } return Streamable.of(findAll(example)).map(conversionFunction).stream(); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count() */ @Override public long count() { return QueryByExampleRedisExecutor.this.count(example); } /* * (non-Javadoc) * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists() */ @Override public boolean exists() { return QueryByExampleRedisExecutor.this.exists(example); } private

Function getConversionFunction(Class inputType, Class

targetType) { if (targetType.isAssignableFrom(inputType)) { return (Function) Function.identity(); } if (targetType.isInterface()) { return o -> projectionFactory.createProjection(targetType, o); } DtoInstantiatingConverter converter = new DtoInstantiatingConverter(targetType, keyValueTemplate.getMappingContext(), entityInstantiators); return o -> (P) converter.convert(o); } } }