feat: 暂存

This commit is contained in:
wangyu 2024-07-03 18:13:23 +08:00
parent 20beef41a4
commit 95e5b1800a
14 changed files with 985 additions and 113 deletions

View File

@ -21,4 +21,11 @@ public abstract class DynamicClassLoader extends URLClassLoader {
* @return 结果
*/
public abstract Map<String, byte[]> getClassBytes();
/**
* 将指定的类二进制替换为其他类
*
* @param path 目标类路径
*/
public abstract void replace(String name, String path);
}

View File

@ -1,7 +1,13 @@
package com.flyfish.framework.compiler.core;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.StreamUtils;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -30,6 +36,21 @@ class MemoryClassLoader extends DynamicClassLoader {
return classBytes;
}
@Override
public void replace(String name, String path) {
String actualPath = "classpath:" + path;
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
Resource[] resources = resolver.getResources(actualPath);
if (ArrayUtils.isNotEmpty(resources)) {
byte[] content = StreamUtils.copyToByteArray(resources[0].getInputStream());
classBytes.put(name, content);
defineClass(name, content);
}
} catch (IOException ignored) {
}
}
/**
* 修复获取package为null的问题定义并验证class
*

View File

@ -2,9 +2,9 @@ package com.flyfish.framework.r2dbc.config;
import com.flyfish.framework.domain.base.Domain;
import com.flyfish.framework.r2dbc.config.callback.ReferenceR2dbcCallback;
import com.flyfish.framework.r2dbc.config.convert.AdditionalConverters;
import com.flyfish.framework.r2dbc.config.convert.FullMappingR2dbcConverter;
import com.flyfish.framework.r2dbc.config.convert.FullReactiveDataAccessStrategy;
import com.flyfish.framework.r2dbc.config.mapping.AdditionalConverters;
import com.flyfish.framework.r2dbc.config.mapping.FullMappingR2dbcConverter;
import com.flyfish.framework.r2dbc.config.mapping.FullReactiveDataAccessStrategy;
import com.flyfish.framework.r2dbc.metadata.R2dbcMetadataManager;
import com.flyfish.framework.r2dbc.metadata.impl.SimpleR2dbcMetadataManager;
import com.flyfish.framework.r2dbc.operations.R2dbcReactiveEntityOperations;

View File

@ -1,99 +0,0 @@
package com.flyfish.framework.r2dbc.config.convert;
import com.flyfish.framework.utils.JacksonUtil;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.mapping.OutboundRow;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.lang.NonNull;
import org.springframework.r2dbc.core.Parameter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.util.Collection;
/**
* 全支持的数据访问策略将数组扁平化转换方便支持json
*
* @author wangyu
*/
@SuppressWarnings("deprecation")
public class FullReactiveDataAccessStrategy extends DefaultReactiveDataAccessStrategy {
private final R2dbcConverter converter;
private final R2dbcDialect dialect;
/**
* 最大化配置的构造器仅提供该实现
*
* @param dialect 别名实现
* @param converter 转换器
*/
public FullReactiveDataAccessStrategy(R2dbcDialect dialect, R2dbcConverter converter) {
super(dialect, converter);
this.converter = converter;
this.dialect = dialect;
}
@Override
@NonNull
public OutboundRow getOutboundRow(@NonNull Object object) {
Assert.notNull(object, "Entity object must not be null");
OutboundRow row = new OutboundRow();
converter.write(object, row);
RelationalPersistentEntity<?> entity = getRequiredPersistentEntity(ClassUtils.getUserClass(object));
for (RelationalPersistentProperty property : entity) {
Parameter value = row.get(property.getColumnName());
if (value != null && shouldConvertArrayValue(property, value)) {
Parameter writeValue = getArrayValue(value);
row.put(property.getColumnName(), writeValue);
}
}
return row;
}
private Parameter getArrayValue(Parameter value) {
if (value.getType().equals(byte[].class)) {
return value;
}
return Parameter.fromOrEmpty(JacksonUtil.toJson(value.getValue()).orElse(null), String.class);
}
/**
* 判断是否需要转换数据值
*
* @param property 属性
* @param value 值表示
* @return 结果
*/
private boolean shouldConvertArrayValue(RelationalPersistentProperty property, Parameter value) {
if (!property.isCollectionLike()) {
return false;
}
if (value.hasValue() && (value.getValue() instanceof Collection || value.getValue().getClass().isArray())) {
return true;
}
if (Collection.class.isAssignableFrom(value.getType()) || value.getType().isArray()) {
return true;
}
return false;
}
@SuppressWarnings("unchecked")
private RelationalPersistentEntity<?> getRequiredPersistentEntity(Class<?> typeToRead) {
return converter.getMappingContext().getRequiredPersistentEntity(typeToRead);
}
}

View File

@ -1,4 +1,4 @@
package com.flyfish.framework.r2dbc.config.convert;
package com.flyfish.framework.r2dbc.config.mapping;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;

View File

@ -1,4 +1,4 @@
package com.flyfish.framework.r2dbc.config.convert;
package com.flyfish.framework.r2dbc.config.mapping;
import com.flyfish.framework.utils.JacksonUtil;
import org.springframework.data.convert.CustomConversions;
@ -9,7 +9,6 @@ import org.springframework.data.r2dbc.convert.MappingR2dbcConverter;
import org.springframework.data.r2dbc.mapping.OutboundRow;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.r2dbc.core.Parameter;

View File

@ -0,0 +1,180 @@
package com.flyfish.framework.r2dbc.config.mapping;
import com.flyfish.framework.utils.JacksonUtil;
import io.r2dbc.spi.Readable;
import io.r2dbc.spi.ReadableMetadata;
import io.r2dbc.spi.Row;
import io.r2dbc.spi.RowMetadata;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.core.DefaultReactiveDataAccessStrategy;
import org.springframework.data.r2dbc.core.PredefinedStatementMapper;
import org.springframework.data.r2dbc.core.ReactiveDataAccessStrategy;
import org.springframework.data.r2dbc.core.StatementMapper;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.mapping.OutboundRow;
import org.springframework.data.r2dbc.query.FullUpdateMapper;
import org.springframework.data.r2dbc.query.UpdateMapper;
import org.springframework.data.relational.core.dialect.RenderContextFactory;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.data.relational.domain.RowDocument;
import org.springframework.lang.NonNull;
import org.springframework.r2dbc.core.Parameter;
import org.springframework.r2dbc.core.PreparedOperation;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import java.util.Collection;
import java.util.List;
import java.util.function.BiFunction;
/**
* 全支持的数据访问策略将数组扁平化转换方便支持json
*
* @author wangyu
*/
@SuppressWarnings("deprecation")
public class FullReactiveDataAccessStrategy implements ReactiveDataAccessStrategy {
private final DefaultReactiveDataAccessStrategy delegated;
private final R2dbcConverter converter;
private final UpdateMapper updateMapper;
private final StatementMapper statementMapper;
/**
* 最大化配置的构造器仅提供该实现
*
* @param dialect 别名实现
* @param converter 转换器
*/
public FullReactiveDataAccessStrategy(R2dbcDialect dialect, R2dbcConverter converter) {
Assert.notNull(dialect, "Dialect must not be null");
Assert.notNull(converter, "RelationalConverter must not be null");
this.delegated = new DefaultReactiveDataAccessStrategy(dialect, converter);
this.converter = converter;
this.updateMapper = new FullUpdateMapper(dialect, converter);
RenderContextFactory factory = new RenderContextFactory(dialect);
this.statementMapper = new PredefinedStatementMapper(dialect, factory.createRenderContext(), this.updateMapper,
delegated.getMappingContext());
}
@Override
@NonNull
public List<SqlIdentifier> getAllColumns(@NonNull Class<?> entityType) {
return delegated.getAllColumns(entityType);
}
@Override
@NonNull
public List<SqlIdentifier> getIdentifierColumns(@NonNull Class<?> entityType) {
return delegated.getIdentifierColumns(entityType);
}
@Override
@NonNull
public OutboundRow getOutboundRow(@NonNull Object object) {
Assert.notNull(object, "Entity object must not be null");
OutboundRow row = new OutboundRow();
converter.write(object, row);
RelationalPersistentEntity<?> entity = getRequiredPersistentEntity(ClassUtils.getUserClass(object));
for (RelationalPersistentProperty property : entity) {
Parameter value = row.get(property.getColumnName());
if (value != null && shouldConvertArrayValue(property, value)) {
Parameter writeValue = getArrayValue(value);
row.put(property.getColumnName(), writeValue);
}
}
return row;
}
@Override
@NonNull
public Parameter getBindValue(@NonNull Parameter value) {
return this.updateMapper.getBindValue(value);
}
@Override
@NonNull
public <T> BiFunction<Row, RowMetadata, T> getRowMapper(@NonNull Class<T> typeToRead) {
return delegated.getRowMapper(typeToRead);
}
@Override
@NonNull
public RowDocument toRowDocument(@NonNull Class<?> type, @NonNull Readable row, @NonNull Iterable<? extends ReadableMetadata> metadata) {
return delegated.toRowDocument(type, row, metadata);
}
@Override
@NonNull
public SqlIdentifier getTableName(@NonNull Class<?> type) {
return delegated.getTableName(type);
}
@Override
@NonNull
public PreparedOperation<?> processNamedParameters(@NonNull String query, @NonNull NamedParameterProvider parameterProvider) {
return delegated.processNamedParameters(query, parameterProvider);
}
private Parameter getArrayValue(Parameter value) {
if (value.getType().equals(byte[].class)) {
return value;
}
return Parameter.fromOrEmpty(JacksonUtil.toJson(value.getValue()).orElse(null), String.class);
}
@Override
@NonNull
public String toSql(@NonNull SqlIdentifier identifier) {
return this.updateMapper.toSql(identifier);
}
@Override
@NonNull
public StatementMapper getStatementMapper() {
return this.statementMapper;
}
@Override
@NonNull
public R2dbcConverter getConverter() {
return converter;
}
/**
* 判断是否需要转换数据值
*
* @param property 属性
* @param value 值表示
* @return 结果
*/
private boolean shouldConvertArrayValue(RelationalPersistentProperty property, Parameter value) {
if (!property.isCollectionLike()) {
return false;
}
if (value.hasValue() && (value.getValue() instanceof Collection || value.getValue().getClass().isArray())) {
return true;
}
if (Collection.class.isAssignableFrom(value.getType()) || value.getType().isArray()) {
return true;
}
return false;
}
@SuppressWarnings("unchecked")
private RelationalPersistentEntity<?> getRequiredPersistentEntity(Class<?> typeToRead) {
return converter.getMappingContext().getRequiredPersistentEntity(typeToRead);
}
}

View File

@ -0,0 +1,145 @@
package com.flyfish.framework.r2dbc.query.adaptor;
import lombok.Getter;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 函数查询
*
* @author wangyu
*/
@Getter
public class FunctionCriteria implements CriteriaDefinition {
private final CriteriaDefinition previous;
private final Combinator combinator;
private final SqlIdentifier column;
private final String functionName;
private final List<Object> values;
private FunctionCriteria(SqlIdentifier column, String functionName, Object... values) {
this(null, null, column, functionName, values);
}
private FunctionCriteria(@Nullable CriteriaDefinition previous, @Nullable Combinator combinator,
SqlIdentifier column, String functionName, Object... values) {
this.previous = previous;
this.combinator = combinator;
this.column = column;
this.functionName = functionName;
this.values = Arrays.asList(values);
}
public static FunctionStep where(String column) {
return new DefaultFunctionStep(column);
}
@Override
public boolean isGroup() {
return false;
}
@Override
@NonNull
public List<CriteriaDefinition> getGroup() {
return Collections.emptyList();
}
@Override
public Comparator getComparator() {
return Comparator.INITIAL;
}
@Override
public Object getValue() {
return values;
}
@Override
public boolean isIgnoreCase() {
return false;
}
@Override
public boolean hasPrevious() {
return null != previous;
}
@Override
public boolean isEmpty() {
return false;
}
public Criteria and() {
return Criteria.empty().and(this);
}
public Criteria or() {
return Criteria.empty().or(this);
}
public FunctionStep and(String column) {
return new DefaultFunctionStep(column, this, Combinator.AND);
}
public FunctionStep or(String column) {
return new DefaultFunctionStep(column, this, Combinator.OR);
}
/**
* 主要操作步骤支持若干函数
*/
public interface FunctionStep {
/**
* json包含mysql特有
*
* @param value
* @return 结果
*/
FunctionCriteria jsonContains(Object value);
}
/**
* 默认的查询链实现
*/
private static class DefaultFunctionStep implements FunctionStep {
private final CriteriaDefinition previous;
private final SqlIdentifier column;
private final Combinator combinator;
private DefaultFunctionStep(String column) {
this(column, Criteria.empty(), Combinator.INITIAL);
}
private DefaultFunctionStep(String column, CriteriaDefinition previous, Combinator combinator) {
this.column = SqlIdentifier.quoted(column);
this.previous = previous;
this.combinator = combinator;
}
@Override
public FunctionCriteria jsonContains(Object value) {
return create(column, "JSON_CONTAINS", value);
}
private FunctionCriteria create(SqlIdentifier column, String functionName, Object... values) {
return new FunctionCriteria(previous, combinator, column, functionName, values);
}
}
}

View File

@ -4,14 +4,8 @@ import com.flyfish.framework.query.Queries;
import com.flyfish.framework.query.spi.adaptor.CriteriaAdaptor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.relational.core.query.Criteria;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.sql.Expression;
import org.springframework.data.relational.core.sql.Functions;
import org.springframework.data.relational.core.sql.SimpleFunction;
import org.springframework.data.relational.core.sql.SqlIdentifier;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
@ -43,12 +37,16 @@ public class R2dbcCriteriaAdaptor implements CriteriaAdaptor<Criteria> {
@Override
public Criteria hasId(String column, Object value) {
return Criteria.where(String.format("JSON_CONTAINS(%s, %s)", column, value)).isTrue();
return FunctionCriteria.where(column)
.jsonContains(value)
.and();
}
@Override
public Criteria has(String column, Object value) {
return Criteria.where(String.format("JSON_CONTAINS(%s, %s)", column, value)).isTrue();
return FunctionCriteria.where(column)
.jsonContains(value)
.and();
}
@Override

View File

@ -0,0 +1,21 @@
package org.springframework.data.r2dbc.core;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.r2dbc.query.UpdateMapper;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.sql.render.RenderContext;
/**
* 创建适配类暴露构造器方法
*
* @author wangyu
*/
public class PredefinedStatementMapper extends DefaultStatementMapper {
public PredefinedStatementMapper(R2dbcDialect dialect, RenderContext renderContext, UpdateMapper updateMapper,
MappingContext<RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext) {
super(dialect, renderContext, updateMapper, mappingContext);
}
}

View File

@ -0,0 +1,408 @@
package org.springframework.data.r2dbc.query;
import com.flyfish.framework.r2dbc.query.adaptor.FunctionCriteria;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.r2dbc.convert.R2dbcConverter;
import org.springframework.data.r2dbc.dialect.R2dbcDialect;
import org.springframework.data.relational.core.dialect.Escaper;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.core.query.CriteriaDefinition;
import org.springframework.data.relational.core.query.ValueFunction;
import org.springframework.data.relational.core.sql.*;
import org.springframework.data.util.Pair;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.r2dbc.core.Parameter;
import org.springframework.r2dbc.core.binding.BindMarker;
import org.springframework.r2dbc.core.binding.BindMarkers;
import org.springframework.r2dbc.core.binding.MutableBindings;
import org.springframework.util.Assert;
import java.util.*;
/**
* 全量支持的mapper
*
* @author wangyu
* 低级重写后续官方支持后删除
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public class FullUpdateMapper extends UpdateMapper {
private final R2dbcDialect dialect;
private final MappingContext<? extends RelationalPersistentEntity<?>, RelationalPersistentProperty> mappingContext;
public FullUpdateMapper(R2dbcDialect dialect, R2dbcConverter converter) {
super(dialect, converter);
this.dialect = dialect;
this.mappingContext = (MappingContext) converter.getMappingContext();
}
/**
* 重写此方法以允许使用自定义函数
*
* @param markers 绑定标记对象必须非空
* @param criteria 需映射的条件定义必须非空
* @param table 必须非空
* @param entity 相关的 {@link RelationalPersistentEntity}可以为空
* @return 结果
*/
@Override
@NonNull
public BoundCondition getMappedObject(@NonNull BindMarkers markers, @NonNull CriteriaDefinition criteria, @NonNull Table table,
@Nullable RelationalPersistentEntity<?> entity) {
Assert.notNull(markers, "BindMarkers must not be null");
Assert.notNull(criteria, "CriteriaDefinition must not be null");
Assert.notNull(table, "Table must not be null");
MutableBindings bindings = new MutableBindings(markers);
if (criteria.isEmpty()) {
throw new IllegalArgumentException("Cannot map empty Criteria");
}
Condition mapped = unroll(criteria, table, entity, bindings);
return new BoundCondition(bindings, mapped);
}
private Condition unroll(CriteriaDefinition criteria, Table table, @Nullable RelationalPersistentEntity<?> entity,
MutableBindings bindings) {
CriteriaDefinition current = criteria;
// reverse unroll criteria chain
Map<CriteriaDefinition, CriteriaDefinition> forwardChain = new HashMap<>();
while (current.hasPrevious()) {
forwardChain.put(current.getPrevious(), current);
current = current.getPrevious();
}
// perform the actual mapping
Condition mapped = getCondition(current, bindings, table, entity);
while (forwardChain.containsKey(current)) {
CriteriaDefinition criterion = forwardChain.get(current);
Condition result = null;
Condition condition = getCondition(criterion, bindings, table, entity);
if (condition != null) {
result = combine(criterion, mapped, criterion.getCombinator(), condition);
}
if (result != null) {
mapped = result;
}
current = criterion;
}
if (mapped == null) {
throw new IllegalStateException("Cannot map empty Criteria");
}
return mapped;
}
@Nullable
private Condition unrollGroup(List<? extends CriteriaDefinition> criteria, Table table,
CriteriaDefinition.Combinator combinator, @Nullable RelationalPersistentEntity<?> entity,
MutableBindings bindings) {
Condition mapped = null;
for (CriteriaDefinition criterion : criteria) {
if (criterion.isEmpty()) {
continue;
}
Condition condition = unroll(criterion, table, entity, bindings);
mapped = combine(criterion, mapped, combinator, condition);
}
return mapped;
}
private Condition combine(CriteriaDefinition criteria, @Nullable Condition currentCondition,
CriteriaDefinition.Combinator combinator, Condition nextCondition) {
if (currentCondition == null) {
currentCondition = nextCondition;
} else if (combinator == CriteriaDefinition.Combinator.INITIAL) {
currentCondition = currentCondition.and(Conditions.nest(nextCondition));
} else if (combinator == CriteriaDefinition.Combinator.AND) {
currentCondition = currentCondition.and(nextCondition);
} else if (combinator == CriteriaDefinition.Combinator.OR) {
currentCondition = currentCondition.or(nextCondition);
} else {
throw new IllegalStateException("Combinator " + combinator + " not supported");
}
return currentCondition;
}
@Nullable
private Condition getCondition(CriteriaDefinition criteria, MutableBindings bindings, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
if (criteria.isEmpty()) {
return null;
}
if (criteria.isGroup()) {
Condition condition = unrollGroup(criteria.getGroup(), table, criteria.getCombinator(), entity, bindings);
return condition == null ? null : Conditions.nest(condition);
}
return mapCondition(criteria, bindings, table, entity);
}
@Nullable
private Object convertValue(CriteriaDefinition.Comparator comparator, @Nullable Object value, TypeInformation<?> typeHint) {
if ((CriteriaDefinition.Comparator.IN.equals(comparator) || CriteriaDefinition.Comparator.NOT_IN.equals(comparator))
&& value instanceof Collection<?> collection && !collection.isEmpty()) {
Collection<Object> mapped = new ArrayList<>(collection.size());
for (Object o : collection) {
mapped.add(convertValue(o, typeHint));
}
return mapped;
}
return convertValue(value, typeHint);
}
/**
* 核心改造方法增加对方法标识的适配剔除额外的column属性
*
* @param criteria 通用查询
* @param bindings 绑定参数
* @param table
* @param entity 实体信息
* @return 结果
*/
private Condition mapCondition(CriteriaDefinition criteria, MutableBindings bindings, Table table,
@Nullable RelationalPersistentEntity<?> entity) {
Field propertyField = createPropertyField(entity, criteria.getColumn(), mappingContext);
Column column = table.column(propertyField.getMappedColumnName());
TypeInformation<?> actualType = propertyField.getTypeHint().getRequiredActualType();
Object mappedValue;
Class<?> typeHint;
CriteriaDefinition.Comparator comparator = criteria.getComparator();
if (criteria.getValue() instanceof Parameter parameter) {
mappedValue = convertValue(comparator, parameter.getValue(), propertyField.getTypeHint());
typeHint = getTypeHint(mappedValue, actualType.getType(), parameter);
} else if (criteria.getValue() instanceof ValueFunction<?> valueFunction) {
mappedValue = valueFunction.map(v -> convertValue(comparator, v, propertyField.getTypeHint()))
.apply(getEscaper(comparator));
typeHint = actualType.getType();
} else {
mappedValue = convertValue(comparator, criteria.getValue(), propertyField.getTypeHint());
typeHint = actualType.getType();
}
// 特殊处理方法
if (criteria instanceof FunctionCriteria functionCriteria) {
return createFunction(column, mappedValue, typeHint, bindings, functionCriteria);
}
// 原处理方法
return createCondition(column, mappedValue, typeHint, bindings, comparator, criteria.isIgnoreCase());
}
/**
* 创建函数引用
*
* @param column 引用的列
* @param mappedValue 映射后的值
* @param valueType 值类型
* @param bindings 绑定上下文
* @param criteria 查询信息
* @return 结果
*/
private Condition createFunction(Column column, @Nullable Object mappedValue, Class<?> valueType,
MutableBindings bindings, FunctionCriteria criteria) {
// 处理多值情况
if (mappedValue instanceof Iterable) {
// 大小 + 1 容纳列名
List<Expression> expressions = new ArrayList<>(
mappedValue instanceof Collection ? ((Collection<?>) mappedValue).size() + 1 : 10);
expressions.add(column);
for (Object o : (Iterable<?>) mappedValue) {
BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
expressions.add(bind(o, valueType, bindings, bindMarker));
}
return FunctionCondition.of(criteria.getFunctionName(), expressions);
} else {
// 单值绑定
BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return FunctionCondition.of(criteria.getFunctionName(), column, expression);
}
}
private Condition createCondition(Column column, @Nullable Object mappedValue, Class<?> valueType,
MutableBindings bindings, CriteriaDefinition.Comparator comparator, boolean ignoreCase) {
if (comparator.equals(CriteriaDefinition.Comparator.IS_NULL)) {
return column.isNull();
}
if (comparator.equals(CriteriaDefinition.Comparator.IS_NOT_NULL)) {
return column.isNotNull();
}
if (comparator == CriteriaDefinition.Comparator.IS_TRUE) {
Expression bind = booleanBind(column, mappedValue, valueType, bindings, ignoreCase);
return column.isEqualTo(bind);
}
if (comparator == CriteriaDefinition.Comparator.IS_FALSE) {
Expression bind = booleanBind(column, mappedValue, valueType, bindings, ignoreCase);
return column.isEqualTo(bind);
}
Expression columnExpression = column;
if (ignoreCase) {
columnExpression = Functions.upper(column);
}
if (comparator == CriteriaDefinition.Comparator.NOT_IN || comparator == CriteriaDefinition.Comparator.IN) {
Condition condition;
if (mappedValue instanceof Iterable) {
List<Expression> expressions = new ArrayList<>(
mappedValue instanceof Collection ? ((Collection<?>) mappedValue).size() : 10);
for (Object o : (Iterable<?>) mappedValue) {
BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
expressions.add(bind(o, valueType, bindings, bindMarker));
}
condition = Conditions.in(columnExpression, expressions.toArray(new Expression[0]));
} else {
BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
condition = Conditions.in(columnExpression, expression);
}
if (comparator == CriteriaDefinition.Comparator.NOT_IN) {
condition = condition.not();
}
return condition;
}
if (comparator == CriteriaDefinition.Comparator.BETWEEN || comparator == CriteriaDefinition.Comparator.NOT_BETWEEN) {
Pair<Object, Object> pair = (Pair<Object, Object>) mappedValue;
Expression begin = bind(pair.getFirst(), valueType, bindings,
bindings.nextMarker(column.getName().getReference()), ignoreCase);
Expression end = bind(pair.getSecond(), valueType, bindings, bindings.nextMarker(column.getName().getReference()),
ignoreCase);
return comparator == CriteriaDefinition.Comparator.BETWEEN ? Conditions.between(columnExpression, begin, end)
: Conditions.notBetween(columnExpression, begin, end);
}
BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
switch (comparator) {
case EQ: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return Conditions.isEqual(columnExpression, expression);
}
case NEQ: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return Conditions.isEqual(columnExpression, expression).not();
}
case LT: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return column.isLess(expression);
}
case LTE: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return column.isLessOrEqualTo(expression);
}
case GT: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return column.isGreater(expression);
}
case GTE: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker);
return column.isGreaterOrEqualTo(expression);
}
case LIKE: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return Conditions.like(columnExpression, expression);
}
case NOT_LIKE: {
Expression expression = bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
return Conditions.notLike(columnExpression, expression);
}
default:
throw new UnsupportedOperationException("Comparator " + comparator + " not supported");
}
}
private Expression booleanBind(Column column, Object mappedValue, Class<?> valueType, MutableBindings bindings,
boolean ignoreCase) {
BindMarker bindMarker = bindings.nextMarker(column.getName().getReference());
return bind(mappedValue, valueType, bindings, bindMarker, ignoreCase);
}
private Expression bind(@Nullable Object mappedValue, Class<?> valueType, MutableBindings bindings,
BindMarker bindMarker) {
return bind(mappedValue, valueType, bindings, bindMarker, false);
}
private Expression bind(@Nullable Object mappedValue, Class<?> valueType, MutableBindings bindings,
BindMarker bindMarker, boolean ignoreCase) {
if (mappedValue != null) {
bindings.bind(bindMarker, mappedValue);
} else {
bindings.bindNull(bindMarker, valueType);
}
return ignoreCase ? Functions.upper(SQL.bindMarker(bindMarker.getPlaceholder()))
: SQL.bindMarker(bindMarker.getPlaceholder());
}
private Escaper getEscaper(CriteriaDefinition.Comparator comparator) {
if (comparator == CriteriaDefinition.Comparator.LIKE || comparator == CriteriaDefinition.Comparator.NOT_LIKE) {
return dialect.getLikeEscaper();
}
return Escaper.DEFAULT;
}
}

View File

@ -0,0 +1,35 @@
package org.springframework.data.relational.core.sql;
import org.springframework.data.relational.core.sql.render.FunctionVisitor;
import java.util.Arrays;
import java.util.List;
/**
* 可以渲染函数的条件
*
* @author wangyu
*/
public class FunctionCondition extends ConstantCondition implements Condition {
private final SimpleFunction function;
private FunctionCondition(SimpleFunction function) {
super("");
this.function = function;
}
public static Condition of(String functionName, List<Expression> args) {
return new FunctionCondition(SimpleFunction.create(functionName, args));
}
public static Condition of(String functionName, Expression... args) {
return of(functionName, Arrays.asList(args));
}
@Override
public String toString() {
FunctionVisitor visitor = new FunctionVisitor();
return function.toString();
}
}

View File

@ -0,0 +1,112 @@
/*
* Copyright 2019-2024 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.core.sql.render;
import org.springframework.data.relational.core.sql.*;
import org.springframework.lang.Nullable;
/**
* {@link org.springframework.data.relational.core.sql.Visitor} delegating {@link Condition} rendering to condition
* {@link org.springframework.data.relational.core.sql.Visitor}s.
*
* @author Mark Paluch
* @author Jens Schauder
* @author Daniele Canteri
* @see AndCondition
* @see OrCondition
* @see IsNull
* @see Comparison
* @see Like
* @see In
* @since 1.1
*/
class ConditionVisitor extends TypedSubtreeVisitor<Condition> implements PartRenderer {
private final RenderContext context;
private final StringBuilder builder = new StringBuilder();
ConditionVisitor(RenderContext context) {
this.context = context;
}
@Override
Delegation enterMatched(Condition segment) {
DelegatingVisitor visitor = getDelegation(segment);
return visitor != null ? Delegation.delegateTo(visitor) : Delegation.retain();
}
@Nullable
private DelegatingVisitor getDelegation(Condition segment) {
if (segment instanceof AndCondition) {
return new MultiConcatConditionVisitor(context, (AndCondition) segment, builder::append);
}
if (segment instanceof OrCondition) {
return new MultiConcatConditionVisitor(context, (OrCondition) segment, builder::append);
}
if (segment instanceof IsNull) {
return new IsNullVisitor(context, builder::append);
}
if (segment instanceof Between) {
return new BetweenVisitor((Between) segment, context, builder::append);
}
if (segment instanceof Comparison) {
return new ComparisonVisitor(context, (Comparison) segment, builder::append);
}
if (segment instanceof Like) {
return new LikeVisitor((Like) segment, context, builder::append);
}
if (segment instanceof In) {
if (((In) segment).hasExpressions()) {
return new InVisitor(context, builder::append);
} else {
return new EmptyInVisitor(context, builder::append);
}
}
if (segment instanceof NestedCondition) {
return new NestedConditionVisitor(context, builder::append);
}
if (segment instanceof ConstantCondition) {
return new ConstantConditionVisitor(context, builder::append);
}
if (segment instanceof Not) {
return new NotConditionVisitor(context, builder::append);
}
if (segment instanceof FunctionCondition functionCondition) {
return new FunctionVisitor(functionCondition, context, builder::append);
}
return null;
}
@Override
public CharSequence getRenderedPart() {
return builder;
}
}

View File

@ -0,0 +1,45 @@
package org.springframework.data.relational.core.sql.render;
import org.springframework.data.relational.core.sql.FunctionCondition;
import org.springframework.data.relational.core.sql.SimpleFunction;
import org.springframework.data.relational.core.sql.Visitable;
import org.springframework.lang.Nullable;
public class FunctionVisitor extends FilteredSubtreeVisitor {
private final RenderContext context;
private final RenderTarget target;
private @Nullable PartRenderer current;
private final StringBuilder part = new StringBuilder();
public FunctionVisitor(FunctionCondition condition, RenderContext context, RenderTarget target) {
super(it -> it == condition);
this.context = context;
this.target = target;
}
@Override
Delegation enterNested(Visitable segment) {
if (segment instanceof SimpleFunction) {
SimpleFunctionVisitor visitor = new SimpleFunctionVisitor(context);
current = visitor;
return Delegation.delegateTo(visitor);
}
throw new IllegalStateException("Cannot provide visitor for " + segment);
}
@Override
Delegation leaveNested(Visitable segment) {
if (current != null) {
part.append(current.getRenderedPart());
current = null;
}
return super.leaveNested(segment);
}
@Override
Delegation leaveMatched(Visitable segment) {
target.onRendered(part);
return super.leaveMatched(segment);
}
}