org.springframework.data.relational.repository.query.RelationalExampleMapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of spring-data-relational Show documentation
Show all versions of spring-data-relational Show documentation
Spring Data Relational support
/*
* 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.relational.repository.query;
import static org.springframework.data.domain.ExampleMatcher.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Example;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.Query;
import org.springframework.data.support.ExampleMatcherAccessor;
import org.springframework.util.Assert;
/**
* Transform an {@link Example} into a {@link Query}.
*
* @since 2.2
* @author Greg Turnquist
*/
public class RelationalExampleMapper {
private final MappingContext, ? extends RelationalPersistentProperty> mappingContext;
public RelationalExampleMapper(
MappingContext, ? extends RelationalPersistentProperty> mappingContext) {
this.mappingContext = mappingContext;
}
/**
* Use the {@link Example} to extract a {@link Query}.
*
* @param example
* @return query
*/
public Query getMappedExample(Example example) {
return getMappedExample(example, mappingContext.getRequiredPersistentEntity(example.getProbeType()));
}
/**
* Transform each property of the {@link Example}'s probe into a {@link Criteria} and assemble them into a
* {@link Query}.
*
* @param example
* @param entity
* @return query
*/
private Query getMappedExample(Example example, RelationalPersistentEntity entity) {
Assert.notNull(example, "Example must not be null!");
Assert.notNull(entity, "RelationalPersistentEntity must not be null!");
PersistentPropertyAccessor propertyAccessor = entity.getPropertyAccessor(example.getProbe());
ExampleMatcherAccessor matcherAccessor = new ExampleMatcherAccessor(example.getMatcher());
final List criteriaBasedOnProperties = new ArrayList<>();
entity.doWithProperties((PropertyHandler) property -> {
if (matcherAccessor.isIgnoredPath(property.getName())) {
return;
}
Optional optionalConvertedPropValue = matcherAccessor //
.getValueTransformerForPath(property.getName()) //
.apply(Optional.ofNullable(propertyAccessor.getProperty(property)));
// If the value is empty, don't try to match against it
if (!optionalConvertedPropValue.isPresent()) {
return;
}
Object convPropValue = optionalConvertedPropValue.get();
boolean ignoreCase = matcherAccessor.isIgnoreCaseForPath(property.getName());
String column = property.getName();
switch (matcherAccessor.getStringMatcherForPath(property.getName())) {
case DEFAULT:
case EXACT:
criteriaBasedOnProperties.add(includeNulls(example) //
? Criteria.where(column).isNull().or(column).is(convPropValue).ignoreCase(ignoreCase)
: Criteria.where(column).is(convPropValue).ignoreCase(ignoreCase));
break;
case ENDING:
criteriaBasedOnProperties.add(includeNulls(example) //
? Criteria.where(column).isNull().or(column).like("%" + convPropValue).ignoreCase(ignoreCase)
: Criteria.where(column).like("%" + convPropValue).ignoreCase(ignoreCase));
break;
case STARTING:
criteriaBasedOnProperties.add(includeNulls(example) //
? Criteria.where(column).isNull().or(column).like(convPropValue + "%").ignoreCase(ignoreCase)
: Criteria.where(column).like(convPropValue + "%").ignoreCase(ignoreCase));
break;
case CONTAINING:
criteriaBasedOnProperties.add(includeNulls(example) //
? Criteria.where(column).isNull().or(column).like("%" + convPropValue + "%").ignoreCase(ignoreCase)
: Criteria.where(column).like("%" + convPropValue + "%").ignoreCase(ignoreCase));
break;
default:
throw new IllegalStateException(example.getMatcher().getDefaultStringMatcher() + " is not supported!");
}
});
// Criteria, assemble!
Criteria criteria = Criteria.empty();
for (Criteria propertyCriteria : criteriaBasedOnProperties) {
if (example.getMatcher().isAllMatching()) {
criteria = criteria.and(propertyCriteria);
} else {
criteria = criteria.or(propertyCriteria);
}
}
return Query.query(criteria);
}
/**
* Does this {@link Example} need to include {@literal NULL} values in its {@link Criteria}?
*
* @param example
* @return whether or not to include nulls.
*/
private static boolean includeNulls(Example example) {
return example.getMatcher().getNullHandler() == NullHandler.INCLUDE;
}
}