/* * Copyright 2014-2017 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 * * http://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.keyvalue.repository.support; import static org.springframework.data.querydsl.QuerydslUtils.QUERY_DSL_PRESENT; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.Optional; import org.springframework.beans.BeanUtils; import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery; import org.springframework.data.keyvalue.repository.query.SpelQueryCreator; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.repository.core.EntityInformation; import org.springframework.data.repository.core.NamedQueries; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.PersistentEntityInformation; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; /** * {@link RepositoryFactorySupport} specific of handing * {@link org.springframework.data.keyvalue.repository.KeyValueRepository}. * * @author Christoph Strobl * @author Oliver Gierke */ public class KeyValueRepositoryFactory extends RepositoryFactorySupport { private static final Class<SpelQueryCreator> DEFAULT_QUERY_CREATOR = SpelQueryCreator.class; private final KeyValueOperations keyValueOperations; private final MappingContext<?, ?> context; private final Class<? extends AbstractQueryCreator<?, ?>> queryCreator; private final Class<? extends RepositoryQuery> repositoryQueryType; /** * Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations}. * * @param keyValueOperations must not be {@literal null}. */ public KeyValueRepositoryFactory(KeyValueOperations keyValueOperations) { this(keyValueOperations, DEFAULT_QUERY_CREATOR); } /** * Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations} and * {@link AbstractQueryCreator}-type. * * @param keyValueOperations must not be {@literal null}. * @param queryCreator defaulted to {@link #DEFAULT_QUERY_CREATOR} if {@literal null}. */ public KeyValueRepositoryFactory(KeyValueOperations keyValueOperations, Class<? extends AbstractQueryCreator<?, ?>> queryCreator) { this(keyValueOperations, queryCreator, KeyValuePartTreeQuery.class); } /** * Creates a new {@link KeyValueRepositoryFactory} for the given {@link KeyValueOperations} and * {@link AbstractQueryCreator}-type. * * @param keyValueOperations must not be {@literal null}. * @param queryCreator must not be {@literal null}. * @param repositoryQueryType must not be {@literal null}. * @since 1.1 */ public KeyValueRepositoryFactory(KeyValueOperations keyValueOperations, Class<? extends AbstractQueryCreator<?, ?>> queryCreator, Class<? extends RepositoryQuery> repositoryQueryType) { Assert.notNull(keyValueOperations, "KeyValueOperations must not be null!"); Assert.notNull(queryCreator, "Query creator type must not be null!"); Assert.notNull(repositoryQueryType, "RepositoryQueryType type must not be null!"); this.queryCreator = queryCreator; this.keyValueOperations = keyValueOperations; this.context = keyValueOperations.getMappingContext(); this.repositoryQueryType = repositoryQueryType; } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getEntityInformation(java.lang.Class) */ @Override @SuppressWarnings("unchecked") public <T, ID> EntityInformation<T, ID> getEntityInformation(Class<T> domainClass) { PersistentEntity<T, ?> entity = (PersistentEntity<T, ?>) context.getPersistentEntity(domainClass).get(); PersistentEntityInformation<T, ID> entityInformation = new PersistentEntityInformation<>(entity); return entityInformation; } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata) */ @Override protected Object getTargetRepository(RepositoryInformation repositoryInformation) { EntityInformation<?, Serializable> entityInformation = getEntityInformation(repositoryInformation.getDomainType()); return super.getTargetRepositoryViaReflection(repositoryInformation, entityInformation, keyValueOperations); } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getRepositoryBaseClass(org.springframework.data.repository.core.RepositoryMetadata) */ @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { return isQueryDslRepository(metadata.getRepositoryInterface()) ? QuerydslKeyValueRepository.class : SimpleKeyValueRepository.class; } /** * Returns whether the given repository interface requires a QueryDsl specific implementation to be chosen. * * @param repositoryInterface must not be {@literal null}. * @return */ private static boolean isQueryDslRepository(Class<?> repositoryInterface) { return QUERY_DSL_PRESENT && QuerydslPredicateExecutor.class.isAssignableFrom(repositoryInterface); } /* * (non-Javadoc) * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider) */ @Override protected Optional<QueryLookupStrategy> getQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider) { return Optional.of(new KeyValueQueryLookupStrategy(key, evaluationContextProvider, this.keyValueOperations, this.queryCreator, this.repositoryQueryType)); } /** * @author Christoph Strobl * @author Oliver Gierke */ private static class KeyValueQueryLookupStrategy implements QueryLookupStrategy { private EvaluationContextProvider evaluationContextProvider; private KeyValueOperations keyValueOperations; private Class<? extends AbstractQueryCreator<?, ?>> queryCreator; private Class<? extends RepositoryQuery> repositoryQueryType; /** * Creates a new {@link KeyValueQueryLookupStrategy} for the given {@link Key}, {@link EvaluationContextProvider}, * {@link KeyValueOperations} and query creator type. * <p> * TODO: Key is not considered. Should it? * * @param key * @param evaluationContextProvider must not be {@literal null}. * @param keyValueOperations must not be {@literal null}. * @param queryCreator must not be {@literal null}. */ public KeyValueQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider, KeyValueOperations keyValueOperations, Class<? extends AbstractQueryCreator<?, ?>> queryCreator) { this(key, evaluationContextProvider, keyValueOperations, queryCreator, KeyValuePartTreeQuery.class); } /** * @param key * @param evaluationContextProvider * @param keyValueOperations * @param queryCreator * @since 1.1 */ public KeyValueQueryLookupStrategy(Key key, EvaluationContextProvider evaluationContextProvider, KeyValueOperations keyValueOperations, Class<? extends AbstractQueryCreator<?, ?>> queryCreator, Class<? extends RepositoryQuery> repositoryQueryType) { Assert.notNull(evaluationContextProvider, "EvaluationContextProvider must not be null!"); Assert.notNull(keyValueOperations, "KeyValueOperations must not be null!"); Assert.notNull(queryCreator, "Query creator type must not be null!"); Assert.notNull(repositoryQueryType, "RepositoryQueryType type must not be null!"); this.evaluationContextProvider = evaluationContextProvider; this.keyValueOperations = keyValueOperations; this.queryCreator = queryCreator; this.repositoryQueryType = repositoryQueryType; } /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries) */ @Override @SuppressWarnings("unchecked") public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) { QueryMethod queryMethod = new QueryMethod(method, metadata, factory); Constructor<? extends KeyValuePartTreeQuery> constructor = (Constructor<? extends KeyValuePartTreeQuery>) ClassUtils .getConstructorIfAvailable(this.repositoryQueryType, QueryMethod.class, EvaluationContextProvider.class, KeyValueOperations.class, Class.class); return BeanUtils.instantiateClass(constructor, queryMethod, evaluationContextProvider, this.keyValueOperations, this.queryCreator); } } }