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

org.springframework.data.jpa.repository.support.FetchableFluentQueryByExample Maven / Gradle / Ivy

There is a newer version: 3.2.5
Show newest version
/*
 * Copyright 2021-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.jpa.repository.support;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.repository.query.EscapeCharacter;
import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.util.Assert;

/**
 * Immutable implementation of {@link FetchableFluentQuery} based on Query by {@link Example}. All methods that return a
 * {@link FetchableFluentQuery} will return a new instance, not the original.
 *
 * @param  Domain type
 * @param  Result type
 * @author Greg Turnquist
 * @author Mark Paluch
 * @author Jens Schauder
 * @author J.R. Onyschak
 * @since 2.6
 */
class FetchableFluentQueryByExample extends FluentQuerySupport implements FetchableFluentQuery {

	private final Example example;
	private final Function> finder;
	private final Function, Long> countOperation;
	private final Function, Boolean> existsOperation;
	private final EntityManager entityManager;
	private final EscapeCharacter escapeCharacter;

	public FetchableFluentQueryByExample(Example example, Function> finder,
			Function, Long> countOperation, Function, Boolean> existsOperation,
			EntityManager entityManager, EscapeCharacter escapeCharacter) {
		this(example, example.getProbeType(), (Class) example.getProbeType(), Sort.unsorted(), Collections.emptySet(),
				finder, countOperation, existsOperation, entityManager, escapeCharacter);
	}

	private FetchableFluentQueryByExample(Example example, Class entityType, Class returnType, Sort sort,
			Collection properties, Function> finder, Function, Long> countOperation,
			Function, Boolean> existsOperation,
			EntityManager entityManager, EscapeCharacter escapeCharacter) {

		super(returnType, sort, properties, entityType);
		this.example = example;
		this.finder = finder;
		this.countOperation = countOperation;
		this.existsOperation = existsOperation;
		this.entityManager = entityManager;
		this.escapeCharacter = escapeCharacter;
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#sortBy(org.springframework.data.domain.Sort)
	 */
	@Override
	public FetchableFluentQuery sortBy(Sort sort) {

		Assert.notNull(sort, "Sort must not be null!");

		return new FetchableFluentQueryByExample<>(example, entityType, resultType, this.sort.and(sort), properties,
				finder, countOperation, existsOperation, entityManager, escapeCharacter);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#as(java.lang.Class)
	 */
	@Override
	public  FetchableFluentQuery as(Class resultType) {

		Assert.notNull(resultType, "Projection target type must not be null!");
		if (!resultType.isInterface()) {
			throw new UnsupportedOperationException("Class-based DTOs are not yet supported.");
		}

		return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, properties, finder,
				countOperation, existsOperation, entityManager, escapeCharacter);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#project(java.util.Collection)
	 */
	@Override
	public FetchableFluentQuery project(Collection properties) {

		return new FetchableFluentQueryByExample<>(example, entityType, resultType, sort, mergeProperties(properties),
				finder, countOperation, existsOperation, entityManager, escapeCharacter);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#oneValue()
	 */
	@Override
	public R oneValue() {

		TypedQuery limitedQuery = createSortedAndProjectedQuery();
		limitedQuery.setMaxResults(2); // Never need more than 2 values

		List results = limitedQuery.getResultList();

		if (results.size() > 1) {
			throw new IncorrectResultSizeDataAccessException(1);
		}

		return results.isEmpty() ? null : getConversionFunction().apply(results.get(0));
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#firstValue()
	 */
	@Override
	public R firstValue() {

		TypedQuery limitedQuery = createSortedAndProjectedQuery();
		limitedQuery.setMaxResults(1); // Never need more than 1 value

		List results = limitedQuery.getResultList();

		return results.isEmpty() ? null : getConversionFunction().apply(results.get(0));
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#all()
	 */
	@Override
	public List all() {

		List resultList = createSortedAndProjectedQuery().getResultList();

		return convert(resultList);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#page(org.springframework.data.domain.Pageable)
	 */
	@Override
	public Page page(Pageable pageable) {
		return pageable.isUnpaged() ? new PageImpl<>(all()) : readPage(pageable);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#stream()
	 */
	@Override
	public Stream stream() {

		return createSortedAndProjectedQuery() //
				.getResultStream() //
				.map(getConversionFunction());
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#count()
	 */
	@Override
	public long count() {
		return countOperation.apply(example);
	}

	/*
	 * (non-Javadoc)
	 * @see org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery#exists()
	 */
	@Override
	public boolean exists() {
		return existsOperation.apply(example);
	}

	private Page readPage(Pageable pageable) {

		TypedQuery pagedQuery = createSortedAndProjectedQuery();

		if (pageable.isPaged()) {
			pagedQuery.setFirstResult((int) pageable.getOffset());
			pagedQuery.setMaxResults(pageable.getPageSize());
		}

		List paginatedResults = convert(pagedQuery.getResultList());

		return PageableExecutionUtils.getPage(paginatedResults, pageable, () -> countOperation.apply(example));
	}

	private TypedQuery createSortedAndProjectedQuery() {

		TypedQuery query = finder.apply(sort);

		if (!properties.isEmpty()) {
			query.setHint(EntityGraphFactory.HINT, EntityGraphFactory.create(entityManager, entityType, properties));
		}

		return query;
	}

	private List convert(List resultList) {

		Function conversionFunction = getConversionFunction();
		List mapped = new ArrayList<>(resultList.size());

		for (S s : resultList) {
			mapped.add(conversionFunction.apply(s));
		}
		return mapped;
	}

	private Function getConversionFunction() {
		return getConversionFunction(example.getProbeType(), resultType);
	}

}