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

org.springframework.data.querydsl.binding.QuerydslBindingsFactory Maven / Gradle / Ivy

There is a newer version: 3.3.0
Show newest version
/*
 * Copyright 2015-2019 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.querydsl.binding;

import java.util.Map;
import java.util.Optional;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;

import com.querydsl.core.types.EntityPath;

/**
 * Factory to create {@link QuerydslBindings} using an {@link EntityPathResolver}.
 *
 * @author Oliver Gierke
 * @author Christoph Strobl
 * @since 1.11
 */
public class QuerydslBindingsFactory implements ApplicationContextAware {

	private static final String INVALID_DOMAIN_TYPE = "Unable to find Querydsl root type for detected domain type %s! User @%s's root attribute to define the domain type manually!";

	private final EntityPathResolver entityPathResolver;
	private final Map, EntityPath> entityPaths;

	private Optional beanFactory;
	private Optional repositories;

	/**
	 * Creates a new {@link QuerydslBindingsFactory} using the given {@link EntityPathResolver}.
	 *
	 * @param entityPathResolver must not be {@literal null}.
	 */
	public QuerydslBindingsFactory(EntityPathResolver entityPathResolver) {

		Assert.notNull(entityPathResolver, "EntityPathResolver must not be null!");

		this.entityPathResolver = entityPathResolver;
		this.entityPaths = new ConcurrentReferenceHashMap<>();
		this.beanFactory = Optional.empty();
		this.repositories = Optional.empty();
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

		this.beanFactory = Optional.of(applicationContext.getAutowireCapableBeanFactory());
		this.repositories = Optional.of(new Repositories(applicationContext));
	}

	/**
	 * Returns the {@link EntityPathResolver} used by the factory.
	 *
	 * @return the entityPathResolver
	 */
	public EntityPathResolver getEntityPathResolver() {
		return entityPathResolver;
	}

	/**
	 * Creates the {@link QuerydslBindings} to be used using for the given domain type. A {@link QuerydslBinderCustomizer}
	 * will be auto-detected.
	 *
	 * @param domainType must not be {@literal null}.
	 * @return will never be {@literal null}.
	 */
	public QuerydslBindings createBindingsFor(TypeInformation domainType) {
		return createBindingsFor(domainType, Optional.empty());
	}

	/**
	 * Creates the {@link QuerydslBindings} to be used using for the given domain type and a pre-defined
	 * {@link QuerydslBinderCustomizer}.
	 *
	 * @param domainType must not be {@literal null}.
	 * @param customizer the {@link QuerydslBinderCustomizer} to use, must not be {@literal null}.
	 * @return will never be {@literal null}.
	 */
	public QuerydslBindings createBindingsFor(TypeInformation domainType,
			Class> customizer) {
		return createBindingsFor(domainType, Optional.of(customizer));
	}

	/**
	 * Creates the {@link QuerydslBindings} to be used using for the given domain type and a pre-defined
	 * {@link QuerydslBinderCustomizer}. If no customizer is given, auto-detection will be applied.
	 *
	 * @param domainType must not be {@literal null}.
	 * @param customizer the {@link QuerydslBinderCustomizer} to use. If an empty {@link Optional} is given customizer
	 *          detection for the given domain type will be applied.
	 * @return
	 */
	private QuerydslBindings createBindingsFor(TypeInformation domainType,
			Optional>> customizer) {

		Assert.notNull(customizer, "Customizer must not be null!");
		Assert.notNull(domainType, "Domain type must not be null!");

		EntityPath path = verifyEntityPathPresent(domainType);

		QuerydslBindings bindings = new QuerydslBindings();
		findCustomizerForDomainType(customizer, domainType.getType()).customize(bindings, path);

		return bindings;
	}

	/**
	 * Tries to detect a Querydsl query type for the given domain type candidate via the configured
	 * {@link EntityPathResolver}.
	 *
	 * @param candidate must not be {@literal null}.
	 * @throws IllegalStateException to indicate the query type can't be found and manual configuration is necessary.
	 */
	private EntityPath verifyEntityPathPresent(TypeInformation candidate) {

		return entityPaths.computeIfAbsent(candidate, key -> {

			try {
				return entityPathResolver.createPath(key.getType());
			} catch (IllegalArgumentException o_O) {
				throw new IllegalStateException(
						String.format(INVALID_DOMAIN_TYPE, key.getType(), QuerydslPredicate.class.getSimpleName()), o_O);
			}
		});
	}

	/**
	 * Obtains the {@link QuerydslBinderCustomizer} for the given domain type. Will inspect the given annotation for a
	 * dedicatedly configured one or consider the domain types's repository.
	 *
	 * @param annotation
	 * @param domainType
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private QuerydslBinderCustomizer> findCustomizerForDomainType(
			Optional> customizer, Class domainType) {

		return customizer//
				.filter(it -> !QuerydslBinderCustomizer.class.equals(it))//
				.map(this::createQuerydslBinderCustomizer)
				.orElseGet(() -> repositories.flatMap(it -> it.getRepositoryFor(domainType))//
						.map(it -> it instanceof QuerydslBinderCustomizer ? (QuerydslBinderCustomizer>) it : null)//
						.orElse(NoOpCustomizer.INSTANCE));
	}

	/**
	 * Obtains a {@link QuerydslBinderCustomizer} for the given type. Will try to obtain a bean from the
	 * {@link org.springframework.beans.factory.BeanFactory} first or fall back to create a fresh instance through the
	 * {@link org.springframework.beans.factory.BeanFactory} or finally falling back to a plain instantiation if no
	 * {@link org.springframework.beans.factory.BeanFactory} is present.
	 *
	 * @param type must not be {@literal null}.
	 * @return
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private QuerydslBinderCustomizer> createQuerydslBinderCustomizer(
			Class type) {

		return beanFactory.map(it -> {

			try {
				return it.getBean(type);
			} catch (NoSuchBeanDefinitionException e) {
				return it.createBean(type);
			}
		}).orElseGet(() -> BeanUtils.instantiateClass(type));
	}

	private static enum NoOpCustomizer implements QuerydslBinderCustomizer> {

		INSTANCE;

		@Override
		public void customize(QuerydslBindings bindings, EntityPath root) {}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy