/* * Copyright 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.ebean.repository.query; import io.ebean.EbeanServer; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Slice; import org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.util.Assert; /** * Set of classes to contain query execution strategies. Depending (mostly) on the return type of a * {@link org.springframework.data.repository.query.QueryMethod} a {@link AbstractStringBasedEbeanQuery} can be executed * in various flavors. * * @author Xuegui Yuan */ public abstract class AbstractEbeanQueryExecution { /** * Executes the given {@link AbstractStringBasedEbeanQuery} with the given {@link ParameterBinder}. * * @param query must not be {@literal null}. * @param values must not be {@literal null}. * @return */ public Object execute(AbstractEbeanQuery query, Object[] values) { Assert.notNull(query, "AbstractEbeanQuery must not be null!"); Assert.notNull(values, "Values must not be null!"); return doExecute(query, values); } /** * Method to implement {@link AbstractStringBasedEbeanQuery} executions by single enum values. * * @param query * @param values * @return */ protected abstract Object doExecute(AbstractEbeanQuery query, Object[] values); /** * Executes the query to return a simple collection of entities. */ static class CollectionExecution extends AbstractEbeanQueryExecution { @Override protected Object doExecute(AbstractEbeanQuery repositoryQuery, Object[] values) { EbeanQueryWrapper createQuery = repositoryQuery.createQuery(values); return createQuery.findList(); } } /** * Executes the query to return a {@link Slice} of entities. * * @author Xuegui Yuan */ static class SlicedExecution extends AbstractEbeanQueryExecution { private final Parameters<?, ?> parameters; /** * Creates a new {@link SlicedExecution} using the given {@link Parameters}. * * @param parameters must not be {@literal null}. */ public SlicedExecution(Parameters<?, ?> parameters) { this.parameters = parameters; } /* * (non-Javadoc) * @see org.springframework.data.ebean.repository.query.AbstractEbeanQueryExecution#doExecute(org.springframework.data.ebean.repository.query.AbstractEbeanQuery, java.lang.Object[]) */ @Override @SuppressWarnings("unchecked") protected Object doExecute(AbstractEbeanQuery query, Object[] values) { ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values); EbeanQueryWrapper createQuery = query.createQuery(values); return createQuery.findSlice(accessor.getPageable()); } } /** * Executes the {@link AbstractStringBasedEbeanQuery} to return a {@link org.springframework.data.domain.Page} of * entities. */ static class PagedExecution extends AbstractEbeanQueryExecution { private final Parameters<?, ?> parameters; public PagedExecution(Parameters<?, ?> parameters) { this.parameters = parameters; } @Override @SuppressWarnings("unchecked") protected Object doExecute(final AbstractEbeanQuery repositoryQuery, final Object[] values) { ParameterAccessor accessor = new ParametersParameterAccessor(parameters, values); EbeanQueryWrapper createQuery = repositoryQuery.createQuery(values); return createQuery.findPage(accessor.getPageable()); } } /** * Executes a {@link AbstractStringBasedEbeanQuery} to return a single entity. */ static class SingleEntityExecution extends AbstractEbeanQueryExecution { @Override protected Object doExecute(AbstractEbeanQuery query, Object[] values) { EbeanQueryWrapper createQuery = query.createQuery(values); return createQuery.findOne(); } } /** * Executes a update query such as an update, insert or delete. */ static class UpdateExecution extends AbstractEbeanQueryExecution { private final EbeanServer ebeanServer; /** * Creates an execution that automatically clears the given {@link EbeanServer} after execution if the given * {@link EbeanServer} is not {@literal null}. * * @param ebeanServer */ public UpdateExecution(EbeanQueryMethod method, EbeanServer ebeanServer) { Class<?> returnType = method.getReturnType(); boolean isVoid = void.class.equals(returnType) || Void.class.equals(returnType); boolean isInt = int.class.equals(returnType) || Integer.class.equals(returnType); Assert.isTrue(isInt || isVoid, "Modifying queries can only use void or int/Integer as return type!"); this.ebeanServer = ebeanServer; } @Override protected Object doExecute(AbstractEbeanQuery query, Object[] values) { EbeanQueryWrapper createQuery = query.createQuery(values); return createQuery.update(); } } /** * {@link AbstractEbeanQueryExecution} removing entities matching the query. * * @author Xuegui Yuan */ static class DeleteExecution extends AbstractEbeanQueryExecution { private final EbeanServer ebeanServer; public DeleteExecution(EbeanServer ebeanServer) { this.ebeanServer = ebeanServer; } /* * (non-Javadoc) * @see org.springframework.data.ebean.repository.query.AbstractEbeanQueryExecution#doExecute(org.springframework.data.ebean.repository.query.AbstractEbeanQuery, java.lang.Object[]) */ @Override protected Object doExecute(AbstractEbeanQuery ebeanQuery, Object[] values) { EbeanQueryWrapper createQuery = ebeanQuery.createQuery(values); return createQuery.delete(); } } /** * {@link AbstractEbeanQueryExecution} performing an exists check on the query. * * @author Xuegui Yuan */ static class ExistsExecution extends AbstractEbeanQueryExecution { @Override protected Object doExecute(AbstractEbeanQuery ebeanQuery, Object[] values) { EbeanQueryWrapper createQuery = ebeanQuery.createQuery(values); return createQuery.isExists(); } } /** * {@link AbstractEbeanQueryExecution} executing a Java 8 Stream. * * @author Xuegui Yuan */ static class StreamExecution extends AbstractEbeanQueryExecution { private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction."; /* * (non-Javadoc) * @see org.springframework.data.ebean.repository.query.AbstractEbeanQueryExecution#doExecute(org.springframework.data.ebean.repository.query.AbstractEbeanQuery, java.lang.Object[]) */ @Override protected Object doExecute(final AbstractEbeanQuery ebeanQuery, Object[] values) { if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) { throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); } EbeanQueryWrapper createQuery = ebeanQuery.createQuery(values); return createQuery.findStream(); } } }