From a5803704f881d534a535f61ee6bb9bbbfdc182a4 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Mon, 26 Feb 2024 15:23:17 +0800 Subject: [PATCH 01/54] =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dorive-api/pom.xml | 2 +- dorive-core/pom.xml | 2 +- dorive-env/pom.xml | 2 +- dorive-event/pom.xml | 2 +- dorive-inject/pom.xml | 2 +- dorive-mybatis-plus/pom.xml | 2 +- dorive-proxy/pom.xml | 2 +- dorive-query/pom.xml | 2 +- dorive-ref/pom.xml | 2 +- dorive-spring-boot-starter/pom.xml | 2 +- dorive-sql/pom.xml | 2 +- dorive-web/pom.xml | 2 +- pom.xml | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/dorive-api/pom.xml b/dorive-api/pom.xml index ca9f7c8a..9755bd70 100644 --- a/dorive-api/pom.xml +++ b/dorive-api/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-api diff --git a/dorive-core/pom.xml b/dorive-core/pom.xml index 23a98eb0..83b29400 100644 --- a/dorive-core/pom.xml +++ b/dorive-core/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-core diff --git a/dorive-env/pom.xml b/dorive-env/pom.xml index 16060c10..64f097ee 100644 --- a/dorive-env/pom.xml +++ b/dorive-env/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-env diff --git a/dorive-event/pom.xml b/dorive-event/pom.xml index 42a09b4c..297735c6 100644 --- a/dorive-event/pom.xml +++ b/dorive-event/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-event diff --git a/dorive-inject/pom.xml b/dorive-inject/pom.xml index 756bed41..b46ff43d 100644 --- a/dorive-inject/pom.xml +++ b/dorive-inject/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-inject diff --git a/dorive-mybatis-plus/pom.xml b/dorive-mybatis-plus/pom.xml index 03be0522..49c9b84a 100644 --- a/dorive-mybatis-plus/pom.xml +++ b/dorive-mybatis-plus/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-mybatis-plus diff --git a/dorive-proxy/pom.xml b/dorive-proxy/pom.xml index 162bdd4c..52aebc36 100644 --- a/dorive-proxy/pom.xml +++ b/dorive-proxy/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-proxy diff --git a/dorive-query/pom.xml b/dorive-query/pom.xml index 032a77af..9da0aa6d 100644 --- a/dorive-query/pom.xml +++ b/dorive-query/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-query diff --git a/dorive-ref/pom.xml b/dorive-ref/pom.xml index 28b80081..8281b7dd 100644 --- a/dorive-ref/pom.xml +++ b/dorive-ref/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-ref diff --git a/dorive-spring-boot-starter/pom.xml b/dorive-spring-boot-starter/pom.xml index d43c948a..6605196a 100644 --- a/dorive-spring-boot-starter/pom.xml +++ b/dorive-spring-boot-starter/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-spring-boot-starter diff --git a/dorive-sql/pom.xml b/dorive-sql/pom.xml index bc1a3471..73926587 100644 --- a/dorive-sql/pom.xml +++ b/dorive-sql/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-sql diff --git a/dorive-web/pom.xml b/dorive-web/pom.xml index d0151dc1..e0ee277b 100644 --- a/dorive-web/pom.xml +++ b/dorive-web/pom.xml @@ -6,7 +6,7 @@ com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 dorive-web diff --git a/pom.xml b/pom.xml index c223ef9a..0ab8cb4c 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.gitee.digital-engine dorive - 3.4.3.3 + 3.4.3.4 pom -- Gitee From 356dc3419bb452a3fa5ccac7717c1358ed44c061 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Wed, 28 Feb 2024 16:35:40 +0800 Subject: [PATCH 02/54] =?UTF-8?q?QueryScan=E6=94=AF=E6=8C=81=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E6=89=AB=E6=8F=8F=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/AbstractQueryRepository.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java b/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java index ad943bed..210ac4c9 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java @@ -37,6 +37,7 @@ import com.gitee.dorive.query.impl.resolver.QueryResolver; import com.gitee.dorive.query.impl.resolver.QueryTypeResolver; import lombok.Data; import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.core.annotation.AnnotatedElementUtils; @@ -58,10 +59,8 @@ public abstract class AbstractQueryRepository extends AbstractEventReposi super.afterPropertiesSet(); Repository repository = AnnotatedElementUtils.getMergedAnnotation(this.getClass(), Repository.class); this.queryScanDef = QueryScanDef.fromElement(this.getClass()); - if (repository != null && this.queryScanDef != null) { - if (StringUtils.isBlank(this.queryScanDef.getRegex())) { - this.queryScanDef.setRegex("^" + getEntityClass().getSimpleName() + ".*"); - } + if (repository != null && queryScanDef != null) { + renewQueryScanDef(); this.mergedRepositoryResolver = new MergedRepositoryResolver(this); this.queryTypeResolver = new QueryTypeResolver(this); this.simpleQueryExecutor = new SimpleQueryExecutor(this); @@ -69,6 +68,20 @@ public abstract class AbstractQueryRepository extends AbstractEventReposi } } + private void renewQueryScanDef() { + String[] value = queryScanDef.getValue(); + String regex = queryScanDef.getRegex(); + Class[] queries = queryScanDef.getQueries(); + if (ArrayUtils.isEmpty(value) && ArrayUtils.isEmpty(queries)) { + String packageName = this.getClass().getPackage().getName(); + String parentPackageName = packageName.substring(0, packageName.lastIndexOf(".")); + queryScanDef.setValue(new String[]{parentPackageName + ".query"}); + } + if (StringUtils.isBlank(regex)) { + queryScanDef.setRegex("^" + getEntityClass().getSimpleName() + ".*"); + } + } + @Override @SuppressWarnings("unchecked") public List selectByQuery(Options options, Object query) { -- Gitee From 7877d2af68da5735b345053ba49342c94040bfb7 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 29 Feb 2024 18:07:46 +0800 Subject: [PATCH 03/54] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=BB=91=E5=AE=9A=E5=9B=BA=E5=AE=9A=E5=80=BC?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E5=8C=BA=E5=88=86=E5=87=BA=E5=BC=BA=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E5=92=8C=E5=BC=B1=E7=BB=91=E5=AE=9A=E7=9A=84=E4=B8=8D?= =?UTF-8?q?=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/core/api/binder/Binder.java | 2 + .../core/impl/binder/AbstractBinder.java | 1 + .../dorive/core/impl/binder/ValueBinder.java | 47 +++++++++++++ .../core/impl/executor/ContextExecutor.java | 2 +- .../impl/joiner/AbstractEntityJoiner.java | 12 ++-- .../core/impl/joiner/MultiEntityJoiner.java | 2 +- .../core/impl/joiner/SingleEntityJoiner.java | 2 +- .../core/impl/joiner/UnionEntityJoiner.java | 2 +- .../core/impl/resolver/BinderResolver.java | 66 +++++++++++-------- .../mybatis/plus/api/CriterionAppender.java | 2 +- .../mybatis/plus/impl/AppenderContext.java | 44 +++++++------ 11 files changed, 121 insertions(+), 61 deletions(-) create mode 100644 dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java b/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java index 116e6fcd..d59778cb 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java @@ -24,6 +24,8 @@ public interface Binder { BindingDef getBindingDef(); + String getFieldName(); + Object getFieldValue(Context context, Object entity); void setFieldValue(Context context, Object entity, Object property); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java index 38c1c43a..2cd10491 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java @@ -37,6 +37,7 @@ public abstract class AbstractBinder implements Binder, Processor { private PropChain fieldPropChain; private Processor processor; + @Override public String getFieldName() { return fieldPropChain.getEntityField().getName(); } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java new file mode 100644 index 00000000..eac14892 --- /dev/null +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.core.impl.binder; + +import cn.hutool.core.convert.Convert; +import com.gitee.dorive.api.entity.def.BindingDef; +import com.gitee.dorive.api.entity.element.PropChain; +import com.gitee.dorive.core.api.binder.Processor; +import com.gitee.dorive.core.api.context.Context; + +public class ValueBinder extends AbstractBinder { + + private final Object value; + + public ValueBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { + super(bindingDef, alias, fieldPropChain, processor); + Class genericType = fieldPropChain.getEntityField().getGenericType(); + String valueStr = bindingDef.getBindExp().substring(1); + this.value = Convert.convert(genericType, valueStr); + } + + @Override + public Object getBoundValue(Context context, Object rootEntity) { + return value; + } + + @Override + public void setBoundValue(Context context, Object rootEntity, Object property) { + // ignore + } + +} diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java index c1d8d701..92fe0e8e 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java @@ -254,7 +254,7 @@ public class ContextExecutor extends AbstractExecutor { } private void getBoundValue(Context context, Object rootEntity, CommonRepository repository, Object entity) { - for (Binder binder : repository.getBinderResolver().getBoundValueBinders()) { + for (Binder binder : repository.getBinderResolver().getPropertyBinders()) { Object fieldValue = binder.getFieldValue(context, entity); if (fieldValue == null) { Object boundValue = binder.getBoundValue(context, rootEntity); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/AbstractEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/AbstractEntityJoiner.java index bbe7b8f9..a169a4a4 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/AbstractEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/AbstractEntityJoiner.java @@ -18,11 +18,11 @@ package com.gitee.dorive.core.impl.joiner; import com.gitee.dorive.api.entity.element.PropChain; +import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.api.executor.EntityJoiner; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.Result; -import com.gitee.dorive.core.impl.binder.ContextBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.CommonRepository; import org.apache.commons.lang3.StringUtils; @@ -51,16 +51,16 @@ public abstract class AbstractEntityJoiner implements EntityJoiner { this.recordIndex = new LinkedHashMap<>(size); } - protected void appendContext(Context context, Example example) { + protected void appendFilterCriteria(Context context, Example example) { if (example == null || example.isEmpty()) { return; } BinderResolver binderResolver = repository.getBinderResolver(); - List contextBinders = binderResolver.getContextBinders(); - for (ContextBinder contextBinder : contextBinders) { - Object boundValue = contextBinder.getBoundValue(context, null); + List weakBinders = binderResolver.getWeakBinders(); + for (Binder binder : weakBinders) { + Object boundValue = binder.getBoundValue(context, null); if (boundValue != null) { - String fieldName = contextBinder.getFieldName(); + String fieldName = binder.getFieldName(); example.eq(fieldName, boundValue); } } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java index 5f2d66fa..c9a8f3a4 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java @@ -49,7 +49,7 @@ public class MultiEntityJoiner extends AbstractEntityJoiner { if (!builder.isEmpty()) { example.getCriteria().add(builder.toCriterion()); } - appendContext(context, example); + appendFilterCriteria(context, example); return example; } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/SingleEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/SingleEntityJoiner.java index 166f981e..928224e5 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/SingleEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/SingleEntityJoiner.java @@ -52,7 +52,7 @@ public class SingleEntityJoiner extends AbstractEntityJoiner { example.in(fieldName, boundValues); } } - appendContext(context, example); + appendFilterCriteria(context, example); return example; } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java index 9a46330f..6dc57047 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java @@ -84,7 +84,7 @@ public class UnionEntityJoiner extends AbstractEntityJoiner { break; } } - appendContext(context, example); + appendFilterCriteria(context, example); return example; } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java index 4298bf01..fe2179e8 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java @@ -21,6 +21,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; import com.gitee.dorive.api.entity.def.BindingDef; import com.gitee.dorive.api.entity.def.EntityDef; import com.gitee.dorive.api.entity.element.EntityEle; @@ -31,6 +32,7 @@ import com.gitee.dorive.core.api.binder.Processor; import com.gitee.dorive.core.entity.option.JoinType; import com.gitee.dorive.core.impl.binder.ContextBinder; import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.ValueBinder; import com.gitee.dorive.core.impl.processor.DefaultProcessor; import com.gitee.dorive.core.impl.processor.PropertyProcessor; import com.gitee.dorive.core.repository.AbstractContextRepository; @@ -55,11 +57,10 @@ public class BinderResolver { private List allBinders; private List propertyBinders; private Map> mergedBindersMap; - private JoinType joinType; - private List selfFields; - private List contextBinders; - private List boundValueBinders; private PropertyBinder boundIdBinder; + private List selfFields; + private JoinType joinType; + private List weakBinders; public BinderResolver(AbstractContextRepository repository, EntityEle entityEle) { this.repository = repository; @@ -73,14 +74,14 @@ public class BinderResolver { String idName = entityEle.getIdName(); List bindingDefs = entityEle.getBindingDefs(); - allBinders = new ArrayList<>(bindingDefs.size()); - propertyBinders = new ArrayList<>(bindingDefs.size()); - mergedBindersMap = new LinkedHashMap<>(bindingDefs.size() * 4 / 3 + 1); - joinType = JoinType.UNKNOWN; - selfFields = new ArrayList<>(bindingDefs.size()); - contextBinders = new ArrayList<>(bindingDefs.size()); - boundValueBinders = new ArrayList<>(bindingDefs.size()); - boundIdBinder = null; + this.allBinders = new ArrayList<>(bindingDefs.size()); + this.propertyBinders = new ArrayList<>(bindingDefs.size()); + this.mergedBindersMap = new LinkedHashMap<>(bindingDefs.size() * 4 / 3 + 1); + this.boundIdBinder = null; + this.selfFields = new ArrayList<>(bindingDefs.size()); + this.joinType = JoinType.UNKNOWN; + this.weakBinders = new ArrayList<>(bindingDefs.size()); + String fieldErrorMsg = "The field configured for @Binding does not exist within the entity! type: {}, field: {}"; for (BindingDef bindingDef : bindingDefs) { bindingDef = renewBindingDef(accessPath, bindingDef); @@ -90,8 +91,7 @@ public class BinderResolver { String alias = entityEle.toAlias(field); PropChain fieldPropChain = propChainMap.get("/" + field); - Assert.notNull(fieldPropChain, "The field configured for @Binding does not exist within the entity! type: {}, field: {}", - genericType.getName(), field); + Assert.notNull(fieldPropChain, fieldErrorMsg, genericType.getName(), field); fieldPropChain.newPropProxy(); Processor processor = newProcessor(bindingDef); @@ -105,27 +105,29 @@ public class BinderResolver { List propertyBinders = mergedBindersMap.computeIfAbsent(belongAccessPath, key -> new ArrayList<>(2)); propertyBinders.add(propertyBinder); - selfFields.add(field); - - if (propertyBinder.isSameType()) { - if (!idName.equals(field)) { - boundValueBinders.add(propertyBinder); - } else { - if (entityDef.getPriority() == 0) { - entityDef.setPriority(-1); - } - boundIdBinder = propertyBinder; + if (propertyBinder.isSameType() && idName.equals(field)) { + if (entityDef.getPriority() == 0) { + entityDef.setPriority(-1); } + boundIdBinder = propertyBinder; } + selfFields.add(field); + + } else if (bindExp.startsWith("$")) { + ValueBinder valueBinder = new ValueBinder(bindingDef, alias, fieldPropChain, processor); + allBinders.add(valueBinder); + weakBinders.add(valueBinder); + } else { ContextBinder contextBinder = new ContextBinder(bindingDef, alias, fieldPropChain, processor); allBinders.add(contextBinder); - contextBinders.add(contextBinder); - boundValueBinders.add(contextBinder); + weakBinders.add(contextBinder); } } + selfFields = Collections.unmodifiableList(selfFields); + if (mergedBindersMap.size() == 1 && mergedBindersMap.containsKey("/")) { List binders = mergedBindersMap.get("/"); boolean hasCollection = CollUtil.findOne(binders, PropertyBinder::isCollection) != null; @@ -136,16 +138,22 @@ public class BinderResolver { if (joinType == JoinType.UNKNOWN) { joinType = JoinType.UNION; } - selfFields = Collections.unmodifiableList(selfFields); } private BindingDef renewBindingDef(String accessPath, BindingDef bindingDef) { bindingDef = BeanUtil.copyProperties(bindingDef, BindingDef.class); - String bindExp = bindingDef.getBindExp(); + + String field = StrUtil.trim(bindingDef.getField()); + Assert.notEmpty(field, "The field of @Binding cannot be empty!"); + bindingDef.setField(field); + + String bindExp = StrUtil.trim(bindingDef.getBindExp()); + Assert.notEmpty(bindExp, "The bindExp of @Binding cannot be empty!"); if (bindExp.startsWith(".")) { bindExp = PathUtils.getAbsolutePath(accessPath, bindExp); - bindingDef.setBindExp(bindExp); } + bindingDef.setBindExp(bindExp); + return bindingDef; } diff --git a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/api/CriterionAppender.java b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/api/CriterionAppender.java index 13e90e54..6ec4c597 100644 --- a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/api/CriterionAppender.java +++ b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/api/CriterionAppender.java @@ -21,6 +21,6 @@ import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; public interface CriterionAppender { - void appendCriterion(AbstractWrapper abstractWrapper, String property, Object value); + void appendCriterion(AbstractWrapper abstractWrapper, int criteriaNum, String property, Object value); } diff --git a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/impl/AppenderContext.java b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/impl/AppenderContext.java index 93e85662..4d64f818 100644 --- a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/impl/AppenderContext.java +++ b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/impl/AppenderContext.java @@ -18,13 +18,13 @@ package com.gitee.dorive.mybatis.plus.impl; import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; -import com.baomidou.mybatisplus.core.conditions.interfaces.Compare; import com.gitee.dorive.api.constant.Operator; import com.gitee.dorive.core.entity.executor.Criterion; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.mybatis.plus.api.CriterionAppender; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -33,44 +33,44 @@ public class AppenderContext { public final static Map OPERATOR_CRITERION_APPENDER_MAP = new ConcurrentHashMap<>(); static { - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.EQ, (abstractWrapper, property, value) -> { + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.EQ, (abstractWrapper, criteriaNum, property, value) -> { if (value instanceof Collection) { abstractWrapper.in(property, (Collection) value); } else { abstractWrapper.eq(property, value); } }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NE, (abstractWrapper, property, value) -> { + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NE, (abstractWrapper, criteriaNum, property, value) -> { if (value instanceof Collection) { abstractWrapper.notIn(property, (Collection) value); } else { abstractWrapper.ne(property, value); } }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.GT, Compare::gt); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.GE, Compare::ge); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LT, Compare::lt); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LE, Compare::le); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IN, (abstractWrapper, property, value) -> abstractWrapper.in(property, (Collection) value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NOT_IN, (abstractWrapper, property, value) -> abstractWrapper.notIn(property, (Collection) value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LIKE, Compare::like); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NOT_LIKE, Compare::notLike); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IS_NULL, (abstractWrapper, property, value) -> abstractWrapper.isNull(property)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IS_NOT_NULL, (abstractWrapper, property, value) -> abstractWrapper.isNotNull(property)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.MULTI_IN, (abstractWrapper, property, value) -> { - String prefix = abstractWrapper.isEmptyOfWhere() ? " WHERE " : " AND "; + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.GT, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.gt(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.GE, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.ge(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LT, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.lt(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LE, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.le(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IN, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.in(property, (Collection) value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NOT_IN, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.notIn(property, (Collection) value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LIKE, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.like(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NOT_LIKE, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.notLike(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IS_NULL, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.isNull(property)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IS_NOT_NULL, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.isNotNull(property)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.MULTI_IN, (abstractWrapper, criteriaNum, property, value) -> { + String prefix = criteriaNum == 1 ? " WHERE " : " AND "; abstractWrapper.last(prefix + "(" + property + ") IN (" + value + ")"); }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.MULTI_NOT_IN, (abstractWrapper, property, value) -> { - String prefix = abstractWrapper.isEmptyOfWhere() ? " WHERE " : " AND "; + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.MULTI_NOT_IN, (abstractWrapper, criteriaNum, property, value) -> { + String prefix = criteriaNum == 1 ? " WHERE " : " AND "; abstractWrapper.last(prefix + "(" + property + ") NOT IN (" + value + ")"); }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.AND, (abstractWrapper, property, value) -> { + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.AND, (abstractWrapper, criteriaNum, property, value) -> { if (value instanceof Example) { abstractWrapper.and(q -> appendCriterion(q, (Example) value)); } }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.OR, (abstractWrapper, property, value) -> { + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.OR, (abstractWrapper, criteriaNum, property, value) -> { if (value instanceof Example) { abstractWrapper.or(q -> appendCriterion(q, (Example) value)); } @@ -78,9 +78,11 @@ public class AppenderContext { } public static void appendCriterion(AbstractWrapper abstractWrapper, Example example) { - for (Criterion criterion : example.getCriteria()) { + List criteria = example.getCriteria(); + int criteriaNum = criteria.size(); + for (Criterion criterion : criteria) { CriterionAppender criterionAppender = OPERATOR_CRITERION_APPENDER_MAP.get(criterion.getOperator()); - criterionAppender.appendCriterion(abstractWrapper, criterion.getProperty(), criterion.getValue()); + criterionAppender.appendCriterion(abstractWrapper, criteriaNum, criterion.getProperty(), criterion.getValue()); } } -- Gitee From 2b28530c8419895721f73feccc0fb7708bc526ef Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Fri, 1 Mar 2024 11:10:46 +0800 Subject: [PATCH 04/54] =?UTF-8?q?=E8=A7=84=E8=8C=83=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/gitee/dorive/core/api/binder/Binder.java | 3 --- .../com/gitee/dorive/core/impl/binder/ContextBinder.java | 5 ++++- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java b/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java index d59778cb..0506b1a5 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java @@ -17,13 +17,10 @@ package com.gitee.dorive.core.api.binder; -import com.gitee.dorive.api.entity.def.BindingDef; import com.gitee.dorive.core.api.context.Context; public interface Binder { - BindingDef getBindingDef(); - String getFieldName(); Object getFieldValue(Context context, Object entity); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java index 30882a1f..6679c773 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java @@ -24,13 +24,16 @@ import com.gitee.dorive.core.api.context.Context; public class ContextBinder extends AbstractBinder { + private final String name; + public ContextBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { super(bindingDef, alias, fieldPropChain, processor); + this.name = bindingDef.getBindExp(); } @Override public Object getBoundValue(Context context, Object rootEntity) { - return context.getAttachment(getBindingDef().getBindExp()); + return context.getAttachment(name); } @Override -- Gitee From 5a64923107e1cc344f041ee89d7ccf51b39ea717 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Fri, 1 Mar 2024 12:54:21 +0800 Subject: [PATCH 05/54] =?UTF-8?q?=E8=A7=84=E8=8C=83=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/core/api/binder/Binder.java | 2 ++ .../core/impl/binder/ContextBinder.java | 5 ++++ .../core/impl/binder/PropertyBinder.java | 27 ++++++++----------- .../dorive/core/impl/binder/ValueBinder.java | 5 ++++ 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java b/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java index 0506b1a5..ffe6d88f 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java @@ -27,6 +27,8 @@ public interface Binder { void setFieldValue(Context context, Object entity, Object property); + String getBoundName(); + Object getBoundValue(Context context, Object rootEntity); void setBoundValue(Context context, Object rootEntity, Object property); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java index 6679c773..27cb4cdd 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java @@ -31,6 +31,11 @@ public class ContextBinder extends AbstractBinder { this.name = bindingDef.getBindExp(); } + @Override + public String getBoundName() { + return name; + } + @Override public Object getBoundValue(Context context, Object rootEntity) { return context.getAttachment(name); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/PropertyBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/PropertyBinder.java index 3bb6dce5..f0b34b4b 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/PropertyBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/PropertyBinder.java @@ -34,14 +34,8 @@ public class PropertyBinder extends AbstractBinder { private PropChain boundPropChain; private String bindAlias; - public PropertyBinder(BindingDef bindingDef, - String alias, - PropChain fieldPropChain, - Processor processor, - String belongAccessPath, - CommonRepository belongRepository, - PropChain boundPropChain, - String bindAlias) { + public PropertyBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor, + String belongAccessPath, CommonRepository belongRepository, PropChain boundPropChain, String bindAlias) { super(bindingDef, alias, fieldPropChain, processor); this.belongAccessPath = belongAccessPath; this.belongRepository = belongRepository; @@ -49,18 +43,11 @@ public class PropertyBinder extends AbstractBinder { this.bindAlias = bindAlias; } + @Override public String getBoundName() { return boundPropChain.getEntityField().getName(); } - public boolean isSameType() { - return getFieldPropChain().isSameType(boundPropChain); - } - - public boolean isCollection() { - return boundPropChain.getEntityField().isCollection(); - } - @Override public Object getBoundValue(Context context, Object rootEntity) { return boundPropChain.getValue(rootEntity); @@ -71,4 +58,12 @@ public class PropertyBinder extends AbstractBinder { boundPropChain.setValue(rootEntity, property); } + public boolean isSameType() { + return getFieldPropChain().isSameType(boundPropChain); + } + + public boolean isCollection() { + return boundPropChain.getEntityField().isCollection(); + } + } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java index eac14892..21928b8f 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java @@ -34,6 +34,11 @@ public class ValueBinder extends AbstractBinder { this.value = Convert.convert(genericType, valueStr); } + @Override + public String getBoundName() { + return null; + } + @Override public Object getBoundValue(Context context, Object rootEntity) { return value; -- Gitee From f133c61a5253f76ef88163fcb34a207f0287875a Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Fri, 1 Mar 2024 17:47:33 +0800 Subject: [PATCH 06/54] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=AE=9A=E4=B9=89@Binding=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=A1=B9=EF=BC=8C=E6=94=AF=E6=8C=81SpEL=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=A2=9E=E5=BC=BA=E7=BB=91=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E7=81=B5=E6=B4=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/api/annotation/Binding.java | 6 +- .../dorive/api/entity/def/BindingDef.java | 3 +- .../core/entity/context/AbstractContext.java | 24 ++-- .../core/impl/binder/AbstractBinder.java | 27 +--- .../core/impl/binder/ContextBinder.java | 49 -------- .../SpELProcessor.java} | 25 ++-- ...{PropertyBinder.java => StrongBinder.java} | 6 +- .../{ValueBinder.java => WeakBinder.java} | 22 ++-- .../core/impl/executor/ContextExecutor.java | 2 +- .../impl/joiner/AbstractEntityJoiner.java | 10 +- .../core/impl/joiner/MultiEntityJoiner.java | 12 +- .../core/impl/joiner/SingleEntityJoiner.java | 8 +- .../core/impl/joiner/UnionEntityJoiner.java | 10 +- .../impl/processor/PropertyProcessor.java | 42 ------- .../core/impl/resolver/BinderResolver.java | 115 ++++++++---------- .../core/repository/CommonRepository.java | 4 +- .../dorive/query/entity/MergedRepository.java | 4 +- .../impl/executor/StepwiseQueryExecutor.java | 31 +++-- .../resolver/MergedRepositoryResolver.java | 16 +-- .../gitee/dorive/sql/impl/SegmentBuilder.java | 14 +-- 20 files changed, 174 insertions(+), 256 deletions(-) delete mode 100644 dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java rename dorive-core/src/main/java/com/gitee/dorive/core/impl/{processor/DefaultProcessor.java => binder/SpELProcessor.java} (56%) rename dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/{PropertyBinder.java => StrongBinder.java} (88%) rename dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/{ValueBinder.java => WeakBinder.java} (75%) delete mode 100644 dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/PropertyProcessor.java diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java index 1af274be..52f0f217 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java @@ -28,9 +28,11 @@ public @interface Binding { String field(); - String bindExp(); + String bindExp() default ""; - String property() default ""; + String processExp() default ""; + + String bindField() default ""; Class processor() default Object.class; diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java b/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java index 4d8070ef..5e3ad9a8 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java @@ -38,7 +38,8 @@ public class BindingDef { private String field; private String bindExp; - private String property; + private String processExp; + private String bindField; private Class processor; public static List fromElement(AnnotatedElement element) { diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/entity/context/AbstractContext.java b/dorive-core/src/main/java/com/gitee/dorive/core/entity/context/AbstractContext.java index d011dcab..82f782a4 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/entity/context/AbstractContext.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/entity/context/AbstractContext.java @@ -20,7 +20,6 @@ package com.gitee.dorive.core.entity.context; import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.api.context.Options; import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.Setter; import java.util.LinkedHashMap; @@ -28,19 +27,23 @@ import java.util.Map; @Getter @Setter -@NoArgsConstructor -public abstract class AbstractContext implements Context { +public abstract class AbstractContext extends LinkedHashMap implements Context { protected Map, Object> options = new LinkedHashMap<>(4); - protected Map attachments = new LinkedHashMap<>(8); + + public AbstractContext() { + super(8); + } public AbstractContext(Options options) { + this(); this.options.putAll(options.getOptions()); } public AbstractContext(Context anotherContext) { + this(); this.options.putAll(anotherContext.getOptions()); - this.attachments.putAll(anotherContext.getAttachments()); + putAll(anotherContext.getAttachments()); } @Override @@ -58,19 +61,24 @@ public abstract class AbstractContext implements Context { options.remove(type); } + @Override + public Map getAttachments() { + return this; + } + @Override public void setAttachment(String name, Object value) { - attachments.put(name, value); + put(name, value); } @Override public Object getAttachment(String name) { - return attachments.get(name); + return get(name); } @Override public void removeAttachment(String name) { - attachments.remove(name); + remove(name); } } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java index 2cd10491..d5f7e25b 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java @@ -25,17 +25,14 @@ import com.gitee.dorive.core.api.context.Context; import lombok.AllArgsConstructor; import lombok.Data; -import java.util.ArrayList; -import java.util.List; - @Data @AllArgsConstructor public abstract class AbstractBinder implements Binder, Processor { - private BindingDef bindingDef; - private String alias; - private PropChain fieldPropChain; - private Processor processor; + protected BindingDef bindingDef; + protected String alias; + protected PropChain fieldPropChain; + protected Processor processor; @Override public String getFieldName() { @@ -54,24 +51,12 @@ public abstract class AbstractBinder implements Binder, Processor { @Override public Object input(Context context, Object value) { - return processor.input(context, value); + return value == null || processor == null ? value : processor.input(context, value); } @Override public Object output(Context context, Object value) { - return processor.output(context, value); - } - - public List collectFieldValues(Context context, List entities) { - List fieldValues = new ArrayList<>(entities.size()); - for (Object entity : entities) { - Object fieldValue = getFieldValue(context, entity); - if (fieldValue != null) { - fieldValue = output(context, fieldValue); - fieldValues.add(fieldValue); - } - } - return fieldValues; + return value == null || processor == null ? value : processor.output(context, value); } } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java deleted file mode 100644 index 27cb4cdd..00000000 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ContextBinder.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 com.gitee.dorive.core.impl.binder; - -import com.gitee.dorive.api.entity.def.BindingDef; -import com.gitee.dorive.api.entity.element.PropChain; -import com.gitee.dorive.core.api.binder.Processor; -import com.gitee.dorive.core.api.context.Context; - -public class ContextBinder extends AbstractBinder { - - private final String name; - - public ContextBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { - super(bindingDef, alias, fieldPropChain, processor); - this.name = bindingDef.getBindExp(); - } - - @Override - public String getBoundName() { - return name; - } - - @Override - public Object getBoundValue(Context context, Object rootEntity) { - return context.getAttachment(name); - } - - @Override - public void setBoundValue(Context context, Object rootEntity, Object property) { - // ignore - } - -} diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/DefaultProcessor.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/SpELProcessor.java similarity index 56% rename from dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/DefaultProcessor.java rename to dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/SpELProcessor.java index 69d1381a..6be5edbb 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/DefaultProcessor.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/SpELProcessor.java @@ -15,23 +15,32 @@ * limitations under the License. */ -package com.gitee.dorive.core.impl.processor; +package com.gitee.dorive.core.impl.binder; import com.gitee.dorive.api.entity.def.BindingDef; import com.gitee.dorive.core.api.binder.Processor; import com.gitee.dorive.core.api.context.Context; -import lombok.Data; -import lombok.NoArgsConstructor; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; -@Data -@NoArgsConstructor -public class DefaultProcessor implements Processor { +public class SpELProcessor implements Processor { - private BindingDef bindingDef; + private final Expression expression; + + public SpELProcessor(BindingDef bindingDef) { + ExpressionParser parser = new SpelExpressionParser(); + this.expression = parser.parseExpression(bindingDef.getProcessExp()); + } @Override public Object input(Context context, Object value) { - return value; + EvaluationContext evaluationContext = new StandardEvaluationContext(); + evaluationContext.setVariable("ctx", context); + evaluationContext.setVariable("val", value); + return expression.getValue(evaluationContext); } @Override diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/PropertyBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java similarity index 88% rename from dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/PropertyBinder.java rename to dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java index f0b34b4b..d414c110 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/PropertyBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java @@ -27,15 +27,15 @@ import lombok.Setter; @Getter @Setter -public class PropertyBinder extends AbstractBinder { +public class StrongBinder extends AbstractBinder { private String belongAccessPath; private CommonRepository belongRepository; private PropChain boundPropChain; private String bindAlias; - public PropertyBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor, - String belongAccessPath, CommonRepository belongRepository, PropChain boundPropChain, String bindAlias) { + public StrongBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor, + String belongAccessPath, CommonRepository belongRepository, PropChain boundPropChain, String bindAlias) { super(bindingDef, alias, fieldPropChain, processor); this.belongAccessPath = belongAccessPath; this.belongRepository = belongRepository; diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/WeakBinder.java similarity index 75% rename from dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java rename to dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/WeakBinder.java index 21928b8f..eec63275 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/WeakBinder.java @@ -17,21 +17,15 @@ package com.gitee.dorive.core.impl.binder; -import cn.hutool.core.convert.Convert; import com.gitee.dorive.api.entity.def.BindingDef; import com.gitee.dorive.api.entity.element.PropChain; import com.gitee.dorive.core.api.binder.Processor; import com.gitee.dorive.core.api.context.Context; -public class ValueBinder extends AbstractBinder { +public class WeakBinder extends AbstractBinder { - private final Object value; - - public ValueBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { + public WeakBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { super(bindingDef, alias, fieldPropChain, processor); - Class genericType = fieldPropChain.getEntityField().getGenericType(); - String valueStr = bindingDef.getBindExp().substring(1); - this.value = Convert.convert(genericType, valueStr); } @Override @@ -41,7 +35,7 @@ public class ValueBinder extends AbstractBinder { @Override public Object getBoundValue(Context context, Object rootEntity) { - return value; + return null; } @Override @@ -49,4 +43,14 @@ public class ValueBinder extends AbstractBinder { // ignore } + @Override + public Object input(Context context, Object value) { + return processor.input(context, value); + } + + @Override + public Object output(Context context, Object value) { + return processor.output(context, value); + } + } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java index 92fe0e8e..a9dd8c0e 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java @@ -254,7 +254,7 @@ public class ContextExecutor extends AbstractExecutor { } private void getBoundValue(Context context, Object rootEntity, CommonRepository repository, Object entity) { - for (Binder binder : repository.getBinderResolver().getPropertyBinders()) { + for (Binder binder : repository.getBinderResolver().getStrongBinders()) { Object fieldValue = binder.getFieldValue(context, entity); if (fieldValue == null) { Object boundValue = binder.getBoundValue(context, rootEntity); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/AbstractEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/AbstractEntityJoiner.java index a169a4a4..5bf19308 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/AbstractEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/AbstractEntityJoiner.java @@ -18,11 +18,11 @@ package com.gitee.dorive.core.impl.joiner; import com.gitee.dorive.api.entity.element.PropChain; -import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.api.executor.EntityJoiner; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.Result; +import com.gitee.dorive.core.impl.binder.WeakBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.CommonRepository; import org.apache.commons.lang3.StringUtils; @@ -56,11 +56,11 @@ public abstract class AbstractEntityJoiner implements EntityJoiner { return; } BinderResolver binderResolver = repository.getBinderResolver(); - List weakBinders = binderResolver.getWeakBinders(); - for (Binder binder : weakBinders) { - Object boundValue = binder.getBoundValue(context, null); + List weakBinders = binderResolver.getWeakBinders(); + for (WeakBinder weakBinder : weakBinders) { + Object boundValue = weakBinder.input(context, null); if (boundValue != null) { - String fieldName = binder.getFieldName(); + String fieldName = weakBinder.getFieldName(); example.eq(fieldName, boundValue); } } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java index c9a8f3a4..c1f58f40 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java @@ -22,7 +22,7 @@ import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; import com.gitee.dorive.core.entity.executor.Result; import com.gitee.dorive.core.impl.binder.AbstractBinder; -import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.repository.CommonRepository; import com.gitee.dorive.core.util.MultiInBuilder; import lombok.Getter; @@ -35,7 +35,7 @@ import java.util.stream.Collectors; @Setter public class MultiEntityJoiner extends AbstractEntityJoiner { - private List binders; + private List binders; public MultiEntityJoiner(CommonRepository repository, int entitiesSize) { super(repository, entitiesSize); @@ -59,11 +59,9 @@ public class MultiEntityJoiner extends AbstractEntityJoiner { for (Object entity : entities) { StringBuilder keyBuilder = new StringBuilder(); - for (PropertyBinder binder : binders) { + for (StrongBinder binder : binders) { Object boundValue = binder.getBoundValue(context, entity); - if (boundValue != null) { - boundValue = binder.input(context, boundValue); - } + boundValue = binder.input(context, boundValue); if (boundValue != null) { multiInBuilder.append(boundValue); String key = boundValue.toString(); @@ -92,7 +90,7 @@ public class MultiEntityJoiner extends AbstractEntityJoiner { List records = result.getRecords(); for (Object entity : records) { StringBuilder keyBuilder = new StringBuilder(); - for (PropertyBinder binder : binders) { + for (StrongBinder binder : binders) { Object fieldValue = binder.getFieldValue(context, entity); if (fieldValue != null) { String key = fieldValue.toString(); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/SingleEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/SingleEntityJoiner.java index 928224e5..aa119a2b 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/SingleEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/SingleEntityJoiner.java @@ -21,7 +21,7 @@ import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; import com.gitee.dorive.core.entity.executor.Result; -import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.repository.CommonRepository; import lombok.Getter; import lombok.Setter; @@ -33,7 +33,7 @@ import java.util.List; @Setter public class SingleEntityJoiner extends AbstractEntityJoiner { - private PropertyBinder binder; + private StrongBinder binder; public SingleEntityJoiner(CommonRepository repository, int entitiesSize) { super(repository, entitiesSize); @@ -60,9 +60,7 @@ public class SingleEntityJoiner extends AbstractEntityJoiner { List boundValues = new ArrayList<>(entities.size()); for (Object entity : entities) { Object boundValue = binder.getBoundValue(context, entity); - if (boundValue != null) { - boundValue = binder.input(context, boundValue); - } + boundValue = binder.input(context, boundValue); if (boundValue != null) { String key = boundValue.toString(); if (!keys.contains(key)) { diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java index 6dc57047..9cc1e40e 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java @@ -23,7 +23,7 @@ import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; import com.gitee.dorive.core.entity.executor.Result; import com.gitee.dorive.core.entity.executor.UnionExample; -import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.repository.CommonRepository; import lombok.Getter; import lombok.Setter; @@ -65,17 +65,15 @@ public class UnionEntityJoiner extends AbstractEntityJoiner { private Example newExample(Context context, Object entity) { Example example = new InnerExample(); - List binders = repository.getBinderResolver().getPropertyBinders(); - for (PropertyBinder binder : binders) { + List binders = repository.getBinderResolver().getStrongBinders(); + for (StrongBinder binder : binders) { Object boundValue = binder.getBoundValue(context, entity); if (boundValue instanceof Collection) { if (((Collection) boundValue).isEmpty()) { boundValue = null; } } - if (boundValue != null) { - boundValue = binder.input(context, boundValue); - } + boundValue = binder.input(context, boundValue); if (boundValue != null) { String fieldName = binder.getFieldName(); example.eq(fieldName, boundValue); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/PropertyProcessor.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/PropertyProcessor.java deleted file mode 100644 index 0bdb69e0..00000000 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/PropertyProcessor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 com.gitee.dorive.core.impl.processor; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import com.gitee.dorive.core.api.context.Context; -import lombok.Data; -import lombok.EqualsAndHashCode; - -import java.util.Collection; - -@Data -@EqualsAndHashCode(callSuper = false) -public class PropertyProcessor extends DefaultProcessor { - - @Override - public Object input(Context context, Object value) { - String property = getBindingDef().getProperty(); - if (value instanceof Collection) { - return CollUtil.map((Collection) value, item -> BeanUtil.getFieldValue(item, property), true); - } else { - return BeanUtil.getFieldValue(value, property); - } - } - -} diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java index fe2179e8..8f143f9b 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java @@ -30,11 +30,9 @@ import com.gitee.dorive.api.impl.resolver.PropChainResolver; import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.api.binder.Processor; import com.gitee.dorive.core.entity.option.JoinType; -import com.gitee.dorive.core.impl.binder.ContextBinder; -import com.gitee.dorive.core.impl.binder.PropertyBinder; -import com.gitee.dorive.core.impl.binder.ValueBinder; -import com.gitee.dorive.core.impl.processor.DefaultProcessor; -import com.gitee.dorive.core.impl.processor.PropertyProcessor; +import com.gitee.dorive.core.impl.binder.StrongBinder; +import com.gitee.dorive.core.impl.binder.SpELProcessor; +import com.gitee.dorive.core.impl.binder.WeakBinder; import com.gitee.dorive.core.repository.AbstractContextRepository; import com.gitee.dorive.core.repository.CommonRepository; import com.gitee.dorive.core.util.PathUtils; @@ -55,12 +53,12 @@ public class BinderResolver { private PropChainResolver propChainResolver; private List allBinders; - private List propertyBinders; - private Map> mergedBindersMap; - private PropertyBinder boundIdBinder; + private List strongBinders; + private Map> mergedBindersMap; + private StrongBinder boundIdBinder; private List selfFields; private JoinType joinType; - private List weakBinders; + private List weakBinders; public BinderResolver(AbstractContextRepository repository, EntityEle entityEle) { this.repository = repository; @@ -75,11 +73,11 @@ public class BinderResolver { List bindingDefs = entityEle.getBindingDefs(); this.allBinders = new ArrayList<>(bindingDefs.size()); - this.propertyBinders = new ArrayList<>(bindingDefs.size()); + this.strongBinders = new ArrayList<>(bindingDefs.size()); this.mergedBindersMap = new LinkedHashMap<>(bindingDefs.size() * 4 / 3 + 1); this.boundIdBinder = null; this.selfFields = new ArrayList<>(bindingDefs.size()); - this.joinType = JoinType.UNKNOWN; + this.joinType = JoinType.UNION; this.weakBinders = new ArrayList<>(bindingDefs.size()); String fieldErrorMsg = "The field configured for @Binding does not exist within the entity! type: {}, field: {}"; @@ -96,100 +94,96 @@ public class BinderResolver { Processor processor = newProcessor(bindingDef); - if (bindExp.startsWith("/")) { - PropertyBinder propertyBinder = newPropertyBinder(bindingDef, alias, fieldPropChain, processor); - allBinders.add(propertyBinder); - propertyBinders.add(propertyBinder); + if (StringUtils.isNotBlank(bindExp)) { + StrongBinder strongBinder = newStrongBinder(bindingDef, alias, fieldPropChain, processor); + allBinders.add(strongBinder); + strongBinders.add(strongBinder); - String belongAccessPath = propertyBinder.getBelongAccessPath(); - List propertyBinders = mergedBindersMap.computeIfAbsent(belongAccessPath, key -> new ArrayList<>(2)); - propertyBinders.add(propertyBinder); + String belongAccessPath = strongBinder.getBelongAccessPath(); + List strongBinders = mergedBindersMap.computeIfAbsent(belongAccessPath, key -> new ArrayList<>(2)); + strongBinders.add(strongBinder); - if (propertyBinder.isSameType() && idName.equals(field)) { + if (strongBinder.isSameType() && idName.equals(field)) { if (entityDef.getPriority() == 0) { entityDef.setPriority(-1); } - boundIdBinder = propertyBinder; + boundIdBinder = strongBinder; } selfFields.add(field); - } else if (bindExp.startsWith("$")) { - ValueBinder valueBinder = new ValueBinder(bindingDef, alias, fieldPropChain, processor); - allBinders.add(valueBinder); - weakBinders.add(valueBinder); - } else { - ContextBinder contextBinder = new ContextBinder(bindingDef, alias, fieldPropChain, processor); - allBinders.add(contextBinder); - weakBinders.add(contextBinder); + WeakBinder weakBinder = new WeakBinder(bindingDef, alias, fieldPropChain, processor); + allBinders.add(weakBinder); + weakBinders.add(weakBinder); } } selfFields = Collections.unmodifiableList(selfFields); if (mergedBindersMap.size() == 1 && mergedBindersMap.containsKey("/")) { - List binders = mergedBindersMap.get("/"); - boolean hasCollection = CollUtil.findOne(binders, PropertyBinder::isCollection) != null; + List binders = mergedBindersMap.get("/"); + boolean hasCollection = CollUtil.findOne(binders, StrongBinder::isCollection) != null; if (!hasCollection) { joinType = binders.size() == 1 ? JoinType.SINGLE : JoinType.MULTI; } } - if (joinType == JoinType.UNKNOWN) { - joinType = JoinType.UNION; - } } private BindingDef renewBindingDef(String accessPath, BindingDef bindingDef) { bindingDef = BeanUtil.copyProperties(bindingDef, BindingDef.class); - String field = StrUtil.trim(bindingDef.getField()); + String bindExp = StrUtil.trim(bindingDef.getBindExp()); + String processExp = StrUtil.trim(bindingDef.getProcessExp()); + String bindField = StrUtil.trim(bindingDef.getBindField()); + Class processor = bindingDef.getProcessor(); Assert.notEmpty(field, "The field of @Binding cannot be empty!"); - bindingDef.setField(field); + Assert.notEmpty(bindExp + processExp, "The expression of @Binding cannot be empty!"); - String bindExp = StrUtil.trim(bindingDef.getBindExp()); - Assert.notEmpty(bindExp, "The bindExp of @Binding cannot be empty!"); if (bindExp.startsWith(".")) { bindExp = PathUtils.getAbsolutePath(accessPath, bindExp); } - bindingDef.setBindExp(bindExp); + if (StringUtils.isNotBlank(bindExp)) { + if (StringUtils.isBlank(processExp) && StringUtils.isBlank(bindField)) { + bindField = PathUtils.getLastName(bindExp); + } + Assert.notEmpty(bindField, "The bindField of @Binding cannot be empty!"); + } + if (StringUtils.isNotBlank(processExp) && processor == Object.class) { + processor = SpELProcessor.class; + } + bindingDef.setField(field); + bindingDef.setBindExp(bindExp); + bindingDef.setProcessExp(processExp); + bindingDef.setBindField(bindField); + bindingDef.setProcessor(processor); return bindingDef; } private Processor newProcessor(BindingDef bindingDef) { Assert.notNull(bindingDef, "The bindingDef cannot be null!"); Class processorClass = bindingDef.getProcessor(); - Processor processor = null; if (processorClass == Object.class) { - if (StringUtils.isBlank(bindingDef.getProperty())) { - processor = new DefaultProcessor(); - } else { - processor = new PropertyProcessor(); - } + return null; + + } else if (processorClass == SpELProcessor.class) { + return new SpELProcessor(bindingDef); + } else { ApplicationContext applicationContext = repository.getApplicationContext(); String[] beanNamesForType = applicationContext.getBeanNamesForType(processorClass); if (beanNamesForType.length > 0) { - processor = (Processor) applicationContext.getBean(beanNamesForType[0]); - } - if (processor == null) { - processor = (Processor) ReflectUtil.newInstance(processorClass); + return (Processor) applicationContext.getBean(beanNamesForType[0]); + } else { + return (Processor) ReflectUtil.newInstance(processorClass); } } - if (processor instanceof DefaultProcessor) { - DefaultProcessor defaultProcessor = (DefaultProcessor) processor; - defaultProcessor.setBindingDef(bindingDef); - } - if (processor instanceof PropertyProcessor) { - Assert.notBlank(bindingDef.getProperty(), "The property of PropertyProcessor cannot be blank!"); - } - return processor; } - private PropertyBinder newPropertyBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { + private StrongBinder newStrongBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { String bindExp = bindingDef.getBindExp(); - String property = bindingDef.getProperty(); + String bindField = bindingDef.getBindField(); Map repositoryMap = repository.getRepositoryMap(); String belongAccessPath = PathUtils.getBelongPath(repositoryMap.keySet(), bindExp); @@ -204,10 +198,9 @@ public class BinderResolver { boundPropChain.newPropProxy(); EntityEle entityEle = belongRepository.getEntityEle(); - String boundName = StringUtils.isBlank(property) ? PathUtils.getLastName(bindExp) : property; - String bindAlias = entityEle.toAlias(boundName); + String bindAlias = entityEle.toAlias(bindField); - return new PropertyBinder(bindingDef, alias, fieldPropChain, processor, + return new StrongBinder(bindingDef, alias, fieldPropChain, processor, belongAccessPath, belongRepository, boundPropChain, bindAlias); } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/repository/CommonRepository.java b/dorive-core/src/main/java/com/gitee/dorive/core/repository/CommonRepository.java index 458272a0..392a8962 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/repository/CommonRepository.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/repository/CommonRepository.java @@ -27,7 +27,7 @@ import com.gitee.dorive.core.entity.executor.OrderBy; import com.gitee.dorive.core.entity.executor.Result; import com.gitee.dorive.core.entity.operation.Query; import com.gitee.dorive.core.entity.option.JoinType; -import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.util.ExampleUtils; import lombok.Getter; @@ -68,7 +68,7 @@ public class CommonRepository extends AbstractProxyRepository implements Matcher return binderResolver.getJoinType(); } - public List getRootBinders() { + public List getRootBinders() { return binderResolver.getMergedBindersMap().get("/"); } diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java b/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java index a4c107f8..fcbe1e96 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java @@ -17,7 +17,7 @@ package com.gitee.dorive.query.entity; -import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.repository.CommonRepository; import lombok.AllArgsConstructor; import lombok.Data; @@ -34,7 +34,7 @@ public class MergedRepository { private String relativeAccessPath; private boolean merged; private CommonRepository definedRepository; - private Map> mergedBindersMap; + private Map> mergedBindersMap; private CommonRepository executedRepository; private Integer order; diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java b/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java index 7654af28..b244b58c 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java @@ -21,7 +21,7 @@ import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; import com.gitee.dorive.core.entity.executor.Result; -import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.CommonRepository; import com.gitee.dorive.core.util.MultiInBuilder; @@ -33,6 +33,7 @@ import com.gitee.dorive.query.repository.AbstractQueryRepository; import lombok.AllArgsConstructor; import lombok.Data; +import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -81,7 +82,7 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { boolean abandoned = exampleWrapper.isAbandoned(); CommonRepository definedRepository = mergedRepository.getDefinedRepository(); - Map> mergedBindersMap = mergedRepository.getMergedBindersMap(); + Map> mergedBindersMap = mergedRepository.getMergedBindersMap(); CommonRepository executedRepository = mergedRepository.getExecutedRepository(); BinderResolver binderResolver = definedRepository.getBinderResolver(); @@ -108,9 +109,9 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { return; } - for (Map.Entry> entry : mergedBindersMap.entrySet()) { + for (Map.Entry> entry : mergedBindersMap.entrySet()) { String relativeAccessPath = entry.getKey(); - List binders = entry.getValue(); + List binders = entry.getValue(); ExampleWrapper targetExampleWrapper = exampleWrapperMap.get(relativeAccessPath); if (targetExampleWrapper != null) { if (entities.isEmpty()) { @@ -119,8 +120,8 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { } Example targetExample = targetExampleWrapper.getExample(); if (binders.size() == 1) { - PropertyBinder binder = binders.get(0); - List fieldValues = binder.collectFieldValues(context, entities); + StrongBinder binder = binders.get(0); + List fieldValues = collectFieldValues(context, entities, binder); if (!fieldValues.isEmpty()) { String boundName = binder.getBoundName(); if (fieldValues.size() == 1) { @@ -133,7 +134,7 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { } } else { - List aliases = binders.stream().map(PropertyBinder::getBindAlias).collect(Collectors.toList()); + List aliases = binders.stream().map(StrongBinder::getBindAlias).collect(Collectors.toList()); MultiInBuilder builder = new MultiInBuilder(aliases, entities.size()); collectFieldValues(context, entities, binders, builder); if (!builder.isEmpty()) { @@ -147,9 +148,21 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { }); } - private void collectFieldValues(Context context, List entities, List binders, MultiInBuilder builder) { + private List collectFieldValues(Context context, List entities, StrongBinder binder) { + List fieldValues = new ArrayList<>(entities.size()); for (Object entity : entities) { - for (PropertyBinder binder : binders) { + Object fieldValue = binder.getFieldValue(context, entity); + if (fieldValue != null) { + fieldValue = binder.output(context, fieldValue); + fieldValues.add(fieldValue); + } + } + return fieldValues; + } + + private void collectFieldValues(Context context, List entities, List binders, MultiInBuilder builder) { + for (Object entity : entities) { + for (StrongBinder binder : binders) { Object fieldValue = binder.getFieldValue(context, entity); if (fieldValue != null) { fieldValue = binder.output(context, fieldValue); diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java b/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java index bcbef8da..49ee4b35 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java @@ -19,7 +19,7 @@ package com.gitee.dorive.query.impl.resolver; import cn.hutool.core.util.StrUtil; import com.gitee.dorive.query.entity.MergedRepository; -import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.AbstractContextRepository; import com.gitee.dorive.core.repository.AbstractRepository; @@ -89,14 +89,14 @@ public class MergedRepositoryResolver { } } - private Map> getMergedBindersMap(String lastAccessPath, CommonRepository repository) { + private Map> getMergedBindersMap(String lastAccessPath, CommonRepository repository) { BinderResolver binderResolver = repository.getBinderResolver(); - List propertyBinders = binderResolver.getPropertyBinders(); - Map> mergedBindersMap = new LinkedHashMap<>(); - for (PropertyBinder propertyBinder : propertyBinders) { - String relativeAccessPath = lastAccessPath + propertyBinder.getBelongAccessPath(); - List existPropertyBinders = mergedBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); - existPropertyBinders.add(propertyBinder); + List strongBinders = binderResolver.getStrongBinders(); + Map> mergedBindersMap = new LinkedHashMap<>(); + for (StrongBinder strongBinder : strongBinders) { + String relativeAccessPath = lastAccessPath + strongBinder.getBelongAccessPath(); + List existStrongBinders = mergedBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); + existStrongBinders.add(strongBinder); } return mergedBindersMap; } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java index e34673e0..e7de6e30 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java @@ -24,7 +24,7 @@ import com.gitee.dorive.core.entity.common.EntityStoreInfo; import com.gitee.dorive.core.entity.executor.Criterion; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; -import com.gitee.dorive.core.impl.binder.PropertyBinder; +import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.impl.executor.ExampleExecutor; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.AbstractContextRepository; @@ -95,12 +95,12 @@ public class SegmentBuilder { String lastAccessPath = mergedRepository.getLastAccessPath(); CommonRepository definedRepository = mergedRepository.getDefinedRepository(); BinderResolver binderResolver = definedRepository.getBinderResolver(); - List propertyBinders = binderResolver.getPropertyBinders(); + List strongBinders = binderResolver.getStrongBinders(); TableSegment tableSegment = node.getTableSegment(); - List onSegments = new ArrayList<>(propertyBinders.size()); - for (PropertyBinder propertyBinder : propertyBinders) { - String relativeAccessPath = lastAccessPath + propertyBinder.getBelongAccessPath(); + List onSegments = new ArrayList<>(strongBinders.size()); + for (StrongBinder strongBinder : strongBinders) { + String relativeAccessPath = lastAccessPath + strongBinder.getBelongAccessPath(); Node targetNode = nodeMap.get(relativeAccessPath); if (targetNode != null) { TableSegment targetTableSegment = targetNode.getTableSegment(); @@ -108,8 +108,8 @@ public class SegmentBuilder { if (!children.contains(node)) { children.add(node); } - OnSegment onSegment = new OnSegment(tableSegment.getTableAlias(), propertyBinder.getAlias(), - targetTableSegment.getTableAlias(), propertyBinder.getBindAlias()); + OnSegment onSegment = new OnSegment(tableSegment.getTableAlias(), strongBinder.getAlias(), + targetTableSegment.getTableAlias(), strongBinder.getBindAlias()); onSegments.add(onSegment); } } -- Gitee From 4947822ae1bc5d6d849fa6376dd51a7b476c783d Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Fri, 1 Mar 2024 17:52:47 +0800 Subject: [PATCH 07/54] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=AE=9A=E4=B9=89@Binding=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E9=A1=B9=EF=BC=8C=E6=94=AF=E6=8C=81SpEL=E8=A1=A8=E8=BE=BE?= =?UTF-8?q?=E5=BC=8F=EF=BC=8C=E5=A2=9E=E5=BC=BA=E7=BB=91=E5=AE=9A=E6=97=B6?= =?UTF-8?q?=E7=9A=84=E7=81=B5=E6=B4=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/core/impl/binder/AbstractBinder.java | 10 ---------- .../gitee/dorive/core/impl/binder/StrongBinder.java | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java index d5f7e25b..cf3b6c08 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java @@ -49,14 +49,4 @@ public abstract class AbstractBinder implements Binder, Processor { fieldPropChain.setValue(entity, property); } - @Override - public Object input(Context context, Object value) { - return value == null || processor == null ? value : processor.input(context, value); - } - - @Override - public Object output(Context context, Object value) { - return value == null || processor == null ? value : processor.output(context, value); - } - } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java index d414c110..19a9b20d 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java @@ -58,6 +58,16 @@ public class StrongBinder extends AbstractBinder { boundPropChain.setValue(rootEntity, property); } + @Override + public Object input(Context context, Object value) { + return value == null || processor == null ? value : processor.input(context, value); + } + + @Override + public Object output(Context context, Object value) { + return value == null || processor == null ? value : processor.output(context, value); + } + public boolean isSameType() { return getFieldPropChain().isSameType(boundPropChain); } -- Gitee From dda6352851638848ca297bf0f6d45c3952a6f199 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Fri, 1 Mar 2024 18:10:28 +0800 Subject: [PATCH 08/54] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java index 9cc1e40e..2af96956 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/UnionEntityJoiner.java @@ -68,12 +68,12 @@ public class UnionEntityJoiner extends AbstractEntityJoiner { List binders = repository.getBinderResolver().getStrongBinders(); for (StrongBinder binder : binders) { Object boundValue = binder.getBoundValue(context, entity); + boundValue = binder.input(context, boundValue); if (boundValue instanceof Collection) { if (((Collection) boundValue).isEmpty()) { boundValue = null; } } - boundValue = binder.input(context, boundValue); if (boundValue != null) { String fieldName = binder.getFieldName(); example.eq(fieldName, boundValue); -- Gitee From 79fe5750b36bc00190ae14f73df3a2b236db700e Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 5 Mar 2024 12:32:36 +0800 Subject: [PATCH 09/54] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/gitee/dorive/api/annotation/Field.java | 4 ++-- .../main/java/com/gitee/dorive/api/entity/def/FieldDef.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Field.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Field.java index 1cf13397..6de4af0c 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Field.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Field.java @@ -35,8 +35,8 @@ public @interface Field { @AliasFor("value") String alias() default ""; - Class converter() default Object.class; - String mapExp() default ""; + Class converter() default Object.class; + } diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/FieldDef.java b/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/FieldDef.java index b4b90f8a..b7f22b44 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/FieldDef.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/FieldDef.java @@ -34,8 +34,8 @@ public class FieldDef { private boolean isId; private String alias; - private Class converter; private String mapExp; + private Class converter; public static FieldDef fromElement(AnnotatedElement element) { Map attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(element, Field.class); -- Gitee From e00815718e68b06a3dff63747d4c06ba85f3b1fd Mon Sep 17 00:00:00 2001 From: chentaoah <609580885@qq.com> Date: Tue, 5 Mar 2024 23:33:03 +0800 Subject: [PATCH 10/54] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91count?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=94=AF=E6=8C=81=E5=A4=9A=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/sql/impl/CountQuerier.java | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java index 39bbd8f4..346a9b86 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java @@ -17,11 +17,12 @@ package com.gitee.dorive.sql.impl; +import cn.hutool.core.collection.CollUtil; import com.gitee.dorive.api.entity.element.EntityEle; import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.query.entity.QueryContext; -import com.gitee.dorive.query.entity.enums.ResultType; import com.gitee.dorive.query.entity.QueryWrapper; +import com.gitee.dorive.query.entity.enums.ResultType; import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; import com.gitee.dorive.sql.entity.SelectSegment; @@ -53,24 +54,45 @@ public class CountQuerier { TableSegment tableSegment = selectSegment.getTableSegment(); List args = selectSegment.getArgs(); String tableAlias = tableSegment.getTableAlias(); + String prefix = tableAlias + "."; EntityEle entityEle = repository.getEntityEle(); - String countByColumn = tableAlias + "." + entityEle.toAlias(countQuery.getCountBy()); - String groupByColumn = tableAlias + "." + entityEle.toAlias(countQuery.getGroupBy()); + List countBy = entityEle.toAliases(countQuery.getCountBy()); + List groupBy = entityEle.toAliases(countQuery.getGroupBy()); + String countByColumns = CollUtil.join(countBy, ",", prefix, null); + String groupByColumns = CollUtil.join(groupBy, ",", prefix, null); - List columns = new ArrayList<>(2); - columns.add(groupByColumn + " AS groupId"); + StringBuilder countByExp = new StringBuilder(); + if (countQuery.isDistinct()) { + countByExp.append("DISTINCT "); + } + if (countBy.size() == 1) { + countByExp.append(countByColumns); - String format = "COUNT(%s) AS total"; - String countByColumnStr = String.format(format, countQuery.isDistinct() ? "DISTINCT " + countByColumn : countByColumn); - columns.add(countByColumnStr); + } else if (countBy.size() > 1) { + countByExp.append("CONCAT(").append(countByColumns).append(")"); + } - selectSegment.setSelectColumns(columns); - selectSegment.setGroupBy("GROUP BY " + groupByColumn); + List selectColumns = new ArrayList<>(2); + selectColumns.add(groupByColumns); + selectColumns.add(String.format("COUNT(%s) AS total", countByExp)); + selectSegment.setSelectColumns(selectColumns); + selectSegment.setGroupBy("GROUP BY " + groupByColumns); List> resultMaps = sqlRunner.selectList(selectSegment.toString(), args.toArray()); Map countMap = new LinkedHashMap<>(resultMaps.size() * 4 / 3 + 1); - resultMaps.forEach(resultMap -> countMap.put(resultMap.get("groupId").toString(), (Long) resultMap.get("total"))); + resultMaps.forEach(resultMap -> { + StringBuilder keyBuilder = new StringBuilder(); + for (String groupByColumn : groupBy) { + String valueStr = String.valueOf(resultMap.get(groupByColumn)).trim(); + keyBuilder.append(valueStr).append(", "); + } + int length = keyBuilder.length(); + if (length > 0) { + keyBuilder.delete(length - 2, length); + } + countMap.put(keyBuilder.toString(), (Long) resultMap.get("total")); + }); return countMap; } @@ -81,8 +103,8 @@ public class CountQuerier { public static class CountQuery { private Object query; private boolean distinct = true; - private String countBy; - private String groupBy; + private List countBy; + private List groupBy; } } -- Gitee From e9a8b861a564c65a2ff53d6bcfcb54e70de53bcc Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Wed, 6 Mar 2024 09:49:43 +0800 Subject: [PATCH 11/54] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gitee/dorive/sql/impl/CountQuerier.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java index 346a9b86..fd04502d 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java @@ -59,23 +59,23 @@ public class CountQuerier { EntityEle entityEle = repository.getEntityEle(); List countBy = entityEle.toAliases(countQuery.getCountBy()); List groupBy = entityEle.toAliases(countQuery.getGroupBy()); - String countByColumns = CollUtil.join(countBy, ",", prefix, null); + String countByExp = CollUtil.join(countBy, ",',',", prefix, null); String groupByColumns = CollUtil.join(groupBy, ",", prefix, null); - StringBuilder countByExp = new StringBuilder(); + StringBuilder expression = new StringBuilder(); if (countQuery.isDistinct()) { - countByExp.append("DISTINCT "); + expression.append("DISTINCT "); } if (countBy.size() == 1) { - countByExp.append(countByColumns); + expression.append(countByExp); } else if (countBy.size() > 1) { - countByExp.append("CONCAT(").append(countByColumns).append(")"); + expression.append("CONCAT(").append(countByExp).append(")"); } List selectColumns = new ArrayList<>(2); selectColumns.add(groupByColumns); - selectColumns.add(String.format("COUNT(%s) AS total", countByExp)); + selectColumns.add(String.format("COUNT(%s) AS total", expression)); selectSegment.setSelectColumns(selectColumns); selectSegment.setGroupBy("GROUP BY " + groupByColumns); -- Gitee From d915ea6195e1d1ba16da16638ff99278d13c9c39 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Wed, 6 Mar 2024 10:04:03 +0800 Subject: [PATCH 12/54] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/sql/entity/CountQuery.java | 45 +++++++++++++++++++ .../sql/entity/{ => segment}/ArgSegment.java | 2 +- .../sql/entity/{ => segment}/JoinSegment.java | 2 +- .../sql/entity/{ => segment}/OnSegment.java | 2 +- .../entity/{ => segment}/SelectSegment.java | 2 +- .../entity/{ => segment}/TableSegment.java | 2 +- .../gitee/dorive/sql/impl/CountQuerier.java | 18 ++------ .../gitee/dorive/sql/impl/SegmentBuilder.java | 6 ++- .../dorive/sql/impl/SqlQueryExecutor.java | 6 +-- 9 files changed, 61 insertions(+), 24 deletions(-) create mode 100644 dorive-sql/src/main/java/com/gitee/dorive/sql/entity/CountQuery.java rename dorive-sql/src/main/java/com/gitee/dorive/sql/entity/{ => segment}/ArgSegment.java (96%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/entity/{ => segment}/JoinSegment.java (95%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/entity/{ => segment}/OnSegment.java (96%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/entity/{ => segment}/SelectSegment.java (98%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/entity/{ => segment}/TableSegment.java (96%) diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/CountQuery.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/CountQuery.java new file mode 100644 index 00000000..be2d695b --- /dev/null +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/CountQuery.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.sql.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Collections; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CountQuery { + + private Object query; + private boolean distinct = true; + private List countBy; + private List groupBy; + + public CountQuery(Object query, String countBy, String groupBy) { + this.query = query; + this.countBy = Collections.singletonList(countBy); + this.groupBy = Collections.singletonList(groupBy); + } + +} diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/ArgSegment.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/ArgSegment.java similarity index 96% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/entity/ArgSegment.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/ArgSegment.java index c56c2079..5c6e7356 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/ArgSegment.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/ArgSegment.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.entity; +package com.gitee.dorive.sql.entity.segment; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/JoinSegment.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/JoinSegment.java similarity index 95% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/entity/JoinSegment.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/JoinSegment.java index f2b9fe25..26aafc9b 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/JoinSegment.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/JoinSegment.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.entity; +package com.gitee.dorive.sql.entity.segment; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/OnSegment.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java similarity index 96% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/entity/OnSegment.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java index 9773da95..e5054516 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/OnSegment.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.entity; +package com.gitee.dorive.sql.entity.segment; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/SelectSegment.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/SelectSegment.java similarity index 98% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/entity/SelectSegment.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/SelectSegment.java index f01c1393..06d51b18 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/SelectSegment.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/SelectSegment.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.entity; +package com.gitee.dorive.sql.entity.segment; import cn.hutool.core.util.StrUtil; import cn.hutool.db.sql.SqlBuilder; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/TableSegment.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/TableSegment.java similarity index 96% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/entity/TableSegment.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/TableSegment.java index 5d563ff7..b51db102 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/TableSegment.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/TableSegment.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.entity; +package com.gitee.dorive.sql.entity.segment; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java index fd04502d..ae64771e 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java @@ -25,12 +25,11 @@ import com.gitee.dorive.query.entity.QueryWrapper; import com.gitee.dorive.query.entity.enums.ResultType; import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; -import com.gitee.dorive.sql.entity.SelectSegment; -import com.gitee.dorive.sql.entity.TableSegment; +import com.gitee.dorive.sql.entity.CountQuery; +import com.gitee.dorive.sql.entity.segment.SelectSegment; +import com.gitee.dorive.sql.entity.segment.TableSegment; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; -import lombok.NoArgsConstructor; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -96,15 +95,4 @@ public class CountQuerier { return countMap; } - @Data - @Builder - @NoArgsConstructor - @AllArgsConstructor - public static class CountQuery { - private Object query; - private boolean distinct = true; - private List countBy; - private List groupBy; - } - } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java index e7de6e30..1c749f57 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java @@ -33,7 +33,11 @@ import com.gitee.dorive.core.util.CriterionUtils; import com.gitee.dorive.query.entity.MergedRepository; import com.gitee.dorive.query.entity.QueryContext; import com.gitee.dorive.query.impl.resolver.QueryResolver; -import com.gitee.dorive.sql.entity.*; +import com.gitee.dorive.sql.entity.segment.ArgSegment; +import com.gitee.dorive.sql.entity.segment.JoinSegment; +import com.gitee.dorive.sql.entity.segment.OnSegment; +import com.gitee.dorive.sql.entity.segment.SelectSegment; +import com.gitee.dorive.sql.entity.segment.TableSegment; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SqlQueryExecutor.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SqlQueryExecutor.java index 98271b65..6f684e27 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SqlQueryExecutor.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SqlQueryExecutor.java @@ -33,9 +33,9 @@ import com.gitee.dorive.query.entity.enums.ResultType; import com.gitee.dorive.query.impl.executor.AbstractQueryExecutor; import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; -import com.gitee.dorive.sql.entity.ArgSegment; -import com.gitee.dorive.sql.entity.SelectSegment; -import com.gitee.dorive.sql.entity.TableSegment; +import com.gitee.dorive.sql.entity.segment.ArgSegment; +import com.gitee.dorive.sql.entity.segment.SelectSegment; +import com.gitee.dorive.sql.entity.segment.TableSegment; import lombok.Getter; import lombok.Setter; -- Gitee From 0d772b2880952771f460cb795a0853a3c4e0c625 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Wed, 6 Mar 2024 10:36:36 +0800 Subject: [PATCH 13/54] =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/MybatisPlusRepository.java | 8 ++--- .../sql/entity/{ => count}/CountQuery.java | 2 +- .../sql/impl/{ => count}/CountQuerier.java | 31 ++++++++++--------- .../impl/{ => executor}/SqlQueryExecutor.java | 3 +- .../impl/{ => executor}/UnionExecutor.java | 2 +- .../impl/{ => segment}/SegmentBuilder.java | 2 +- 6 files changed, 26 insertions(+), 22 deletions(-) rename dorive-sql/src/main/java/com/gitee/dorive/sql/entity/{ => count}/CountQuery.java (97%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/impl/{ => count}/CountQuerier.java (82%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/impl/{ => executor}/SqlQueryExecutor.java (98%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/impl/{ => executor}/UnionExecutor.java (99%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/impl/{ => segment}/SegmentBuilder.java (99%) diff --git a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java index de7641a7..9e1a4e9e 100644 --- a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java +++ b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java @@ -35,10 +35,10 @@ import com.gitee.dorive.query.entity.QueryContext; import com.gitee.dorive.query.entity.QueryWrapper; import com.gitee.dorive.ref.repository.AbstractRefRepository; import com.gitee.dorive.sql.api.SqlRunner; -import com.gitee.dorive.sql.impl.CountQuerier; -import com.gitee.dorive.sql.impl.SegmentBuilder; -import com.gitee.dorive.sql.impl.SqlQueryExecutor; -import com.gitee.dorive.sql.impl.UnionExecutor; +import com.gitee.dorive.sql.impl.count.CountQuerier; +import com.gitee.dorive.sql.impl.segment.SegmentBuilder; +import com.gitee.dorive.sql.impl.executor.SqlQueryExecutor; +import com.gitee.dorive.sql.impl.executor.UnionExecutor; import lombok.Data; import lombok.EqualsAndHashCode; import org.apache.commons.lang3.StringUtils; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/CountQuery.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java similarity index 97% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/entity/CountQuery.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java index be2d695b..083df183 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/CountQuery.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.entity; +package com.gitee.dorive.sql.entity.count; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java similarity index 82% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java index ae64771e..397bf19a 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.impl; +package com.gitee.dorive.sql.impl.count; import cn.hutool.core.collection.CollUtil; import com.gitee.dorive.api.entity.element.EntityEle; @@ -25,9 +25,10 @@ import com.gitee.dorive.query.entity.QueryWrapper; import com.gitee.dorive.query.entity.enums.ResultType; import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; -import com.gitee.dorive.sql.entity.CountQuery; +import com.gitee.dorive.sql.entity.count.CountQuery; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; +import com.gitee.dorive.sql.impl.segment.SegmentBuilder; import lombok.AllArgsConstructor; import lombok.Data; @@ -80,19 +81,21 @@ public class CountQuerier { List> resultMaps = sqlRunner.selectList(selectSegment.toString(), args.toArray()); Map countMap = new LinkedHashMap<>(resultMaps.size() * 4 / 3 + 1); - resultMaps.forEach(resultMap -> { - StringBuilder keyBuilder = new StringBuilder(); - for (String groupByColumn : groupBy) { - String valueStr = String.valueOf(resultMap.get(groupByColumn)).trim(); - keyBuilder.append(valueStr).append(", "); - } - int length = keyBuilder.length(); - if (length > 0) { - keyBuilder.delete(length - 2, length); - } - countMap.put(keyBuilder.toString(), (Long) resultMap.get("total")); - }); + resultMaps.forEach(resultMap -> countMap.put(buildKey(resultMap, groupBy), (Long) resultMap.get("total"))); return countMap; } + private String buildKey(Map resultMap, List groupBy) { + StringBuilder keyBuilder = new StringBuilder(); + for (String column : groupBy) { + String valueStr = String.valueOf(resultMap.get(column)).trim(); + keyBuilder.append(valueStr).append(", "); + } + int length = keyBuilder.length(); + if (length > 0) { + keyBuilder.delete(length - 2, length); + } + return keyBuilder.toString(); + } + } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SqlQueryExecutor.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java similarity index 98% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SqlQueryExecutor.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java index 6f684e27..a74c2214 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SqlQueryExecutor.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.impl; +package com.gitee.dorive.sql.impl.executor; import cn.hutool.core.collection.CollUtil; import com.gitee.dorive.api.entity.element.EntityEle; @@ -36,6 +36,7 @@ import com.gitee.dorive.sql.api.SqlRunner; import com.gitee.dorive.sql.entity.segment.ArgSegment; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; +import com.gitee.dorive.sql.impl.segment.SegmentBuilder; import lombok.Getter; import lombok.Setter; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/UnionExecutor.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/UnionExecutor.java similarity index 99% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/impl/UnionExecutor.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/UnionExecutor.java index f9db3dba..a6122243 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/UnionExecutor.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/UnionExecutor.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.impl; +package com.gitee.dorive.sql.impl.executor; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java similarity index 99% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index 1c749f57..b75033d7 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.impl; +package com.gitee.dorive.sql.impl.segment; import com.gitee.dorive.api.constant.Operator; import com.gitee.dorive.api.entity.element.EntityEle; -- Gitee From a5b7e0460dfa2f35a38cb54ba592a5f8da53ce13 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Wed, 6 Mar 2024 16:13:31 +0800 Subject: [PATCH 14/54] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=8C=87=E5=AE=9A=E4=BB=BB=E6=84=8F=E8=A1=A8?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=BD=9C=E4=B8=BAcount=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sql/entity/context/SegmentContext.java | 53 +++++++++++++++++++ .../sql/entity/context/SegmentInfo.java | 30 +++++++++++ .../dorive/sql/entity/count/CountQuery.java | 8 +++ .../dorive/sql/impl/count/CountQuerier.java | 53 +++++++++++++------ .../sql/impl/executor/SqlQueryExecutor.java | 3 +- .../sql/impl/segment/SegmentBuilder.java | 11 +++- 6 files changed, 138 insertions(+), 20 deletions(-) create mode 100644 dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentContext.java create mode 100644 dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentContext.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentContext.java new file mode 100644 index 00000000..a9a7af1e --- /dev/null +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentContext.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.sql.entity.context; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +@Data +@NoArgsConstructor +public class SegmentContext { + + private Set selectNames = new LinkedHashSet<>(2); + private Map nameSegmentInfoMap = new LinkedHashMap<>(8); + + public void selectName(String name) { + if (name != null) { + selectNames.add(name); + } + } + + public boolean isSelected(String name) { + return selectNames.contains(name); + } + + public void put(String name, SegmentInfo segmentInfo) { + nameSegmentInfoMap.put(name, segmentInfo); + } + + public SegmentInfo get(String name) { + return nameSegmentInfoMap.get(name); + } + +} diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java new file mode 100644 index 00000000..c2bfb581 --- /dev/null +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.sql.entity.context; + +import com.gitee.dorive.api.entity.element.EntityEle; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class SegmentInfo { + private EntityEle entityEle; + private String tableName; + private String tableAlias; +} diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java index 083df183..c4cd4185 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java @@ -33,6 +33,7 @@ public class CountQuery { private Object query; private boolean distinct = true; + private String name; private List countBy; private List groupBy; @@ -42,4 +43,11 @@ public class CountQuery { this.groupBy = Collections.singletonList(groupBy); } + public CountQuery(Object query, String name, String countBy, String groupBy) { + this.query = query; + this.name = name; + this.countBy = Collections.singletonList(countBy); + this.groupBy = Collections.singletonList(groupBy); + } + } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java index 397bf19a..0648aa13 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java @@ -25,12 +25,15 @@ import com.gitee.dorive.query.entity.QueryWrapper; import com.gitee.dorive.query.entity.enums.ResultType; import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; +import com.gitee.dorive.sql.entity.context.SegmentInfo; import com.gitee.dorive.sql.entity.count.CountQuery; +import com.gitee.dorive.sql.entity.context.SegmentContext; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; import com.gitee.dorive.sql.impl.segment.SegmentBuilder; import lombok.AllArgsConstructor; import lombok.Data; +import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -50,32 +53,24 @@ public class CountQuerier { QueryWrapper queryWrapper = new QueryWrapper(countQuery.getQuery()); repository.resolveQuery(queryContext, queryWrapper); - SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext); + SegmentContext segmentContext = new SegmentContext(); + segmentContext.selectName(countQuery.getName()); + SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext, segmentContext); TableSegment tableSegment = selectSegment.getTableSegment(); List args = selectSegment.getArgs(); - String tableAlias = tableSegment.getTableAlias(); - String prefix = tableAlias + "."; + String tableAlias = tableSegment.getTableAlias(); EntityEle entityEle = repository.getEntityEle(); - List countBy = entityEle.toAliases(countQuery.getCountBy()); - List groupBy = entityEle.toAliases(countQuery.getGroupBy()); - String countByExp = CollUtil.join(countBy, ",',',", prefix, null); - String groupByColumns = CollUtil.join(groupBy, ",", prefix, null); - StringBuilder expression = new StringBuilder(); - if (countQuery.isDistinct()) { - expression.append("DISTINCT "); - } - if (countBy.size() == 1) { - expression.append(countByExp); + String countByExp = buildCountByExp(countQuery, segmentContext, tableAlias, entityEle); - } else if (countBy.size() > 1) { - expression.append("CONCAT(").append(countByExp).append(")"); - } + String groupByPrefix = tableAlias + "."; + List groupBy = entityEle.toAliases(countQuery.getGroupBy()); + String groupByColumns = CollUtil.join(groupBy, ",", groupByPrefix, null); List selectColumns = new ArrayList<>(2); selectColumns.add(groupByColumns); - selectColumns.add(String.format("COUNT(%s) AS total", expression)); + selectColumns.add(String.format("COUNT(%s) AS total", countByExp)); selectSegment.setSelectColumns(selectColumns); selectSegment.setGroupBy("GROUP BY " + groupByColumns); @@ -85,6 +80,30 @@ public class CountQuerier { return countMap; } + private String buildCountByExp(CountQuery countQuery, SegmentContext segmentContext, String tableAlias, EntityEle entityEle) { + String name = countQuery.getName(); + if (StringUtils.isNotBlank(name)) { + SegmentInfo segmentInfo = segmentContext.get(name); + tableAlias = segmentInfo.getTableAlias(); + entityEle = segmentInfo.getEntityEle(); + } + String countByPrefix = tableAlias + "."; + List countBy = entityEle.toAliases(countQuery.getCountBy()); + String countByStr = CollUtil.join(countBy, ",',',", countByPrefix, null); + + StringBuilder countByExp = new StringBuilder(); + if (countQuery.isDistinct()) { + countByExp.append("DISTINCT "); + } + if (countBy.size() == 1) { + countByExp.append(countByStr); + + } else if (countBy.size() > 1) { + countByExp.append("CONCAT(").append(countByStr).append(")"); + } + return countByExp.toString(); + } + private String buildKey(Map resultMap, List groupBy) { StringBuilder keyBuilder = new StringBuilder(); for (String column : groupBy) { diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java index a74c2214..9e8e97ad 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java @@ -34,6 +34,7 @@ import com.gitee.dorive.query.impl.executor.AbstractQueryExecutor; import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; import com.gitee.dorive.sql.entity.segment.ArgSegment; +import com.gitee.dorive.sql.entity.context.SegmentContext; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; import com.gitee.dorive.sql.impl.segment.SegmentBuilder; @@ -74,7 +75,7 @@ public class SqlQueryExecutor extends AbstractQueryExecutor { boolean needCount = queryContext.isNeedCount(); Result emptyResult = queryContext.newEmptyResult(); - SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext); + SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext, new SegmentContext()); char letter = selectSegment.getLetter(); TableSegment tableSegment = selectSegment.getTableSegment(); List argSegments = selectSegment.getArgSegments(); diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index b75033d7..fa9fad29 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -33,9 +33,11 @@ import com.gitee.dorive.core.util.CriterionUtils; import com.gitee.dorive.query.entity.MergedRepository; import com.gitee.dorive.query.entity.QueryContext; import com.gitee.dorive.query.impl.resolver.QueryResolver; +import com.gitee.dorive.sql.entity.context.SegmentInfo; import com.gitee.dorive.sql.entity.segment.ArgSegment; import com.gitee.dorive.sql.entity.segment.JoinSegment; import com.gitee.dorive.sql.entity.segment.OnSegment; +import com.gitee.dorive.sql.entity.context.SegmentContext; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; import lombok.AllArgsConstructor; @@ -46,7 +48,7 @@ import java.util.*; @Data public class SegmentBuilder { - public SelectSegment buildSegment(QueryContext queryContext) { + public SelectSegment buildSegment(QueryContext queryContext, SegmentContext segmentContext) { Context context = queryContext.getContext(); QueryResolver queryResolver = queryContext.getQueryResolver(); Map exampleMap = queryContext.getExampleMap(); @@ -59,8 +61,10 @@ public class SegmentBuilder { for (MergedRepository mergedRepository : mergedRepositories) { String absoluteAccessPath = mergedRepository.getAbsoluteAccessPath(); String relativeAccessPath = mergedRepository.getRelativeAccessPath(); + CommonRepository definedRepository = mergedRepository.getDefinedRepository(); CommonRepository executedRepository = mergedRepository.getExecutedRepository(); + String name = definedRepository.getName(); EntityEle entityEle = executedRepository.getEntityEle(); EntityStoreInfo entityStoreInfo = AbstractContextRepository.getEntityStoreInfo(entityEle); ExampleExecutor exampleExecutor = AbstractContextRepository.getExampleExecutor(entityEle); @@ -70,7 +74,10 @@ public class SegmentBuilder { Example example = exampleMap.computeIfAbsent(absoluteAccessPath, key -> new InnerExample(Collections.emptyList())); exampleExecutor.convert(context, example); - TableSegment tableSegment = new TableSegment(tableName, tableAlias, example.isNotEmpty(), new ArrayList<>(example.getCriteria().size())); + boolean isJoin = example.isNotEmpty() || segmentContext.isSelected(name); + segmentContext.put(name, new SegmentInfo(entityEle, tableName, tableAlias)); + + TableSegment tableSegment = new TableSegment(tableName, tableAlias, isJoin, new ArrayList<>(example.getCriteria().size())); Node node = new Node(tableSegment, new ArrayList<>(4)); nodeMap.put(relativeAccessPath, node); -- Gitee From ff9ac6e59c6ba8d28b1ef42b64a54392fdc336b3 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Wed, 6 Mar 2024 16:15:39 +0800 Subject: [PATCH 15/54] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=8C=87=E5=AE=9A=E4=BB=BB=E6=84=8F=E8=A1=A8?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=BD=9C=E4=B8=BAcount=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gitee/dorive/sql/entity/context/SegmentInfo.java | 3 +-- .../java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java index c2bfb581..bd830890 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java @@ -24,7 +24,6 @@ import lombok.Data; @Data @AllArgsConstructor public class SegmentInfo { - private EntityEle entityEle; - private String tableName; private String tableAlias; + private EntityEle entityEle; } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index fa9fad29..015a0b4f 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -75,7 +75,7 @@ public class SegmentBuilder { exampleExecutor.convert(context, example); boolean isJoin = example.isNotEmpty() || segmentContext.isSelected(name); - segmentContext.put(name, new SegmentInfo(entityEle, tableName, tableAlias)); + segmentContext.put(name, new SegmentInfo(tableAlias, entityEle)); TableSegment tableSegment = new TableSegment(tableName, tableAlias, isJoin, new ArrayList<>(example.getCriteria().size())); Node node = new Node(tableSegment, new ArrayList<>(4)); -- Gitee From f02ef9b3e9c30501a4ffca771ea0e91749596858 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Mon, 11 Mar 2024 18:53:53 +0800 Subject: [PATCH 16/54] =?UTF-8?q?=E5=B0=86SegmentBuilder=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E5=A4=9A=E4=BE=8B=EF=BC=8C=E5=B9=B6=E7=AE=80=E5=8C=96?= =?UTF-8?q?count=E9=80=89=E5=8F=96=E6=97=B6=E7=9A=84=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E4=BB=8Econtext=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/MybatisPlusRepository.java | 6 +-- .../sql/entity/context/SegmentContext.java | 53 ------------------- .../dorive/sql/entity/count/CountQuery.java | 8 --- .../dorive/sql/impl/count/CountQuerier.java | 18 +++---- .../sql/impl/executor/SqlQueryExecutor.java | 8 ++- .../sql/impl/segment/SegmentBuilder.java | 28 +++++++--- 6 files changed, 31 insertions(+), 90 deletions(-) delete mode 100644 dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentContext.java diff --git a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java index 9e1a4e9e..79edb74f 100644 --- a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java +++ b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java @@ -36,7 +36,6 @@ import com.gitee.dorive.query.entity.QueryWrapper; import com.gitee.dorive.ref.repository.AbstractRefRepository; import com.gitee.dorive.sql.api.SqlRunner; import com.gitee.dorive.sql.impl.count.CountQuerier; -import com.gitee.dorive.sql.impl.segment.SegmentBuilder; import com.gitee.dorive.sql.impl.executor.SqlQueryExecutor; import com.gitee.dorive.sql.impl.executor.UnionExecutor; import lombok.Data; @@ -62,9 +61,8 @@ public class MybatisPlusRepository extends AbstractRefRepository { public void afterPropertiesSet() throws Exception { ImplFactory implFactory = getApplicationContext().getBean(ImplFactory.class); this.sqlRunner = implFactory.getInstance(SqlRunner.class); - SegmentBuilder segmentBuilder = new SegmentBuilder(); - this.sqlQueryExecutor = new SqlQueryExecutor(this, segmentBuilder, this.sqlRunner); - this.countQuerier = new CountQuerier(this, segmentBuilder, this.sqlRunner); + this.sqlQueryExecutor = new SqlQueryExecutor(this, this.sqlRunner); + this.countQuerier = new CountQuerier(this, this.sqlRunner); super.afterPropertiesSet(); } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentContext.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentContext.java deleted file mode 100644 index a9a7af1e..00000000 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentContext.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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 com.gitee.dorive.sql.entity.context; - -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -@Data -@NoArgsConstructor -public class SegmentContext { - - private Set selectNames = new LinkedHashSet<>(2); - private Map nameSegmentInfoMap = new LinkedHashMap<>(8); - - public void selectName(String name) { - if (name != null) { - selectNames.add(name); - } - } - - public boolean isSelected(String name) { - return selectNames.contains(name); - } - - public void put(String name, SegmentInfo segmentInfo) { - nameSegmentInfoMap.put(name, segmentInfo); - } - - public SegmentInfo get(String name) { - return nameSegmentInfoMap.get(name); - } - -} diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java index c4cd4185..083df183 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java @@ -33,7 +33,6 @@ public class CountQuery { private Object query; private boolean distinct = true; - private String name; private List countBy; private List groupBy; @@ -43,11 +42,4 @@ public class CountQuery { this.groupBy = Collections.singletonList(groupBy); } - public CountQuery(Object query, String name, String countBy, String groupBy) { - this.query = query; - this.name = name; - this.countBy = Collections.singletonList(countBy); - this.groupBy = Collections.singletonList(groupBy); - } - } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java index 0648aa13..713d6cdf 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java @@ -27,13 +27,11 @@ import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; import com.gitee.dorive.sql.entity.context.SegmentInfo; import com.gitee.dorive.sql.entity.count.CountQuery; -import com.gitee.dorive.sql.entity.context.SegmentContext; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; import com.gitee.dorive.sql.impl.segment.SegmentBuilder; import lombok.AllArgsConstructor; import lombok.Data; -import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -45,7 +43,6 @@ import java.util.Map; public class CountQuerier { private AbstractQueryRepository repository; - private SegmentBuilder segmentBuilder; private SqlRunner sqlRunner; public Map selectCountMap(Context context, CountQuery countQuery) { @@ -53,16 +50,14 @@ public class CountQuerier { QueryWrapper queryWrapper = new QueryWrapper(countQuery.getQuery()); repository.resolveQuery(queryContext, queryWrapper); - SegmentContext segmentContext = new SegmentContext(); - segmentContext.selectName(countQuery.getName()); - SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext, segmentContext); + SegmentBuilder segmentBuilder = new SegmentBuilder(); + SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext); TableSegment tableSegment = selectSegment.getTableSegment(); List args = selectSegment.getArgs(); String tableAlias = tableSegment.getTableAlias(); EntityEle entityEle = repository.getEntityEle(); - - String countByExp = buildCountByExp(countQuery, segmentContext, tableAlias, entityEle); + String countByExp = buildCountByExp(countQuery, segmentBuilder, tableAlias, entityEle); String groupByPrefix = tableAlias + "."; List groupBy = entityEle.toAliases(countQuery.getGroupBy()); @@ -80,10 +75,9 @@ public class CountQuerier { return countMap; } - private String buildCountByExp(CountQuery countQuery, SegmentContext segmentContext, String tableAlias, EntityEle entityEle) { - String name = countQuery.getName(); - if (StringUtils.isNotBlank(name)) { - SegmentInfo segmentInfo = segmentContext.get(name); + private String buildCountByExp(CountQuery countQuery, SegmentBuilder segmentBuilder, String tableAlias, EntityEle entityEle) { + SegmentInfo segmentInfo = segmentBuilder.getFirstMatched(); + if (segmentInfo != null) { tableAlias = segmentInfo.getTableAlias(); entityEle = segmentInfo.getEntityEle(); } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java index 9e8e97ad..e3896ff4 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java @@ -34,7 +34,6 @@ import com.gitee.dorive.query.impl.executor.AbstractQueryExecutor; import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; import com.gitee.dorive.sql.entity.segment.ArgSegment; -import com.gitee.dorive.sql.entity.context.SegmentContext; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; import com.gitee.dorive.sql.impl.segment.SegmentBuilder; @@ -49,12 +48,10 @@ import java.util.Map; @Setter public class SqlQueryExecutor extends AbstractQueryExecutor { - private SegmentBuilder segmentBuilder; private SqlRunner sqlRunner; - public SqlQueryExecutor(AbstractQueryRepository repository, SegmentBuilder segmentBuilder, SqlRunner sqlRunner) { + public SqlQueryExecutor(AbstractQueryRepository repository, SqlRunner sqlRunner) { super(repository); - this.segmentBuilder = segmentBuilder; this.sqlRunner = sqlRunner; } @@ -75,7 +72,8 @@ public class SqlQueryExecutor extends AbstractQueryExecutor { boolean needCount = queryContext.isNeedCount(); Result emptyResult = queryContext.newEmptyResult(); - SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext, new SegmentContext()); + SegmentBuilder segmentBuilder = new SegmentBuilder(); + SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext); char letter = selectSegment.getLetter(); TableSegment tableSegment = selectSegment.getTableSegment(); List argSegments = selectSegment.getArgSegments(); diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index 015a0b4f..87ef5e8e 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -37,18 +37,24 @@ import com.gitee.dorive.sql.entity.context.SegmentInfo; import com.gitee.dorive.sql.entity.segment.ArgSegment; import com.gitee.dorive.sql.entity.segment.JoinSegment; import com.gitee.dorive.sql.entity.segment.OnSegment; -import com.gitee.dorive.sql.entity.context.SegmentContext; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; import lombok.AllArgsConstructor; import lombok.Data; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; @Data public class SegmentBuilder { - public SelectSegment buildSegment(QueryContext queryContext, SegmentContext segmentContext) { + private SegmentInfo firstMatched; + private List allMatched = new ArrayList<>(8); + + public SelectSegment buildSegment(QueryContext queryContext) { Context context = queryContext.getContext(); QueryResolver queryResolver = queryContext.getQueryResolver(); Map exampleMap = queryContext.getExampleMap(); @@ -64,20 +70,26 @@ public class SegmentBuilder { CommonRepository definedRepository = mergedRepository.getDefinedRepository(); CommonRepository executedRepository = mergedRepository.getExecutedRepository(); - String name = definedRepository.getName(); EntityEle entityEle = executedRepository.getEntityEle(); EntityStoreInfo entityStoreInfo = AbstractContextRepository.getEntityStoreInfo(entityEle); ExampleExecutor exampleExecutor = AbstractContextRepository.getExampleExecutor(entityEle); String tableName = entityStoreInfo.getTableName(); String tableAlias = selectSegment.generateTableAlias(); + + SegmentInfo segmentInfo = new SegmentInfo(tableAlias, entityEle); + boolean isMatch = definedRepository.matches(context); + if (isMatch) { + if (firstMatched == null) { + firstMatched = segmentInfo; + } + allMatched.add(segmentInfo); + } + Example example = exampleMap.computeIfAbsent(absoluteAccessPath, key -> new InnerExample(Collections.emptyList())); exampleExecutor.convert(context, example); - boolean isJoin = example.isNotEmpty() || segmentContext.isSelected(name); - segmentContext.put(name, new SegmentInfo(tableAlias, entityEle)); - - TableSegment tableSegment = new TableSegment(tableName, tableAlias, isJoin, new ArrayList<>(example.getCriteria().size())); + TableSegment tableSegment = new TableSegment(tableName, tableAlias, example.isNotEmpty() || isMatch, new ArrayList<>(example.getCriteria().size())); Node node = new Node(tableSegment, new ArrayList<>(4)); nodeMap.put(relativeAccessPath, node); -- Gitee From 205b0c60635174bcc873243833d8b8cd060fe5ea Mon Sep 17 00:00:00 2001 From: chentaoah <609580885@qq.com> Date: Tue, 12 Mar 2024 01:11:41 +0800 Subject: [PATCH 17/54] =?UTF-8?q?=E6=94=AF=E6=8C=81Selector=E7=9A=84count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dorive/core/api/context/Matcher.java | 2 +- .../core/impl/context/SelectTypeMatcher.java | 18 ++++----- .../core/repository/CommonRepository.java | 5 ++- .../dorive/sql/entity/count/CountQuery.java | 2 + .../dorive/sql/impl/count/CountQuerier.java | 9 +++-- .../sql/impl/executor/SqlQueryExecutor.java | 4 +- .../sql/impl/segment/SegmentBuilder.java | 37 ++++++++----------- 7 files changed, 38 insertions(+), 39 deletions(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/api/context/Matcher.java b/dorive-core/src/main/java/com/gitee/dorive/core/api/context/Matcher.java index d71bd7c4..c76dcdf8 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/api/context/Matcher.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/api/context/Matcher.java @@ -19,6 +19,6 @@ package com.gitee.dorive.core.api.context; public interface Matcher { - boolean matches(Context context); + boolean matches(Options options); } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/context/SelectTypeMatcher.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/context/SelectTypeMatcher.java index d9f3048e..965dda7d 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/context/SelectTypeMatcher.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/context/SelectTypeMatcher.java @@ -17,8 +17,8 @@ package com.gitee.dorive.core.impl.context; -import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.api.context.Matcher; +import com.gitee.dorive.core.api.context.Options; import com.gitee.dorive.core.api.context.Selector; import com.gitee.dorive.core.entity.option.SelectType; import com.gitee.dorive.core.repository.CommonRepository; @@ -41,19 +41,19 @@ public class SelectTypeMatcher implements Matcher { public SelectTypeMatcher(CommonRepository repository) { this.repository = repository; - this.matcherMap.put(SelectType.NONE, context -> false); - this.matcherMap.put(SelectType.ROOT, context -> repository.isRoot()); - this.matcherMap.put(SelectType.ALL, context -> true); + this.matcherMap.put(SelectType.NONE, options -> false); + this.matcherMap.put(SelectType.ROOT, options -> repository.isRoot()); + this.matcherMap.put(SelectType.ALL, options -> true); this.matcherMap.put(SelectType.SELECTOR, new SelectorMatcher()); } @Override - public boolean matches(Context context) { - SelectType selectType = (SelectType) context.getOption(SelectType.class); + public boolean matches(Options options) { + SelectType selectType = (SelectType) options.getOption(SelectType.class); if (selectType != null) { Matcher matcher = matcherMap.get(selectType); if (matcher != null) { - return matcher.matches(context); + return matcher.matches(options); } } return false; @@ -61,8 +61,8 @@ public class SelectTypeMatcher implements Matcher { private class SelectorMatcher implements Matcher { @Override - public boolean matches(Context context) { - Selector selector = (Selector) context.getOption(Selector.class); + public boolean matches(Options options) { + Selector selector = (Selector) options.getOption(Selector.class); if (selector != null) { Set names = selector.getNames(); String name = repository.getName(); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/repository/CommonRepository.java b/dorive-core/src/main/java/com/gitee/dorive/core/repository/CommonRepository.java index 392a8962..4289705b 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/repository/CommonRepository.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/repository/CommonRepository.java @@ -20,6 +20,7 @@ package com.gitee.dorive.core.repository; import com.gitee.dorive.api.entity.element.PropChain; import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.api.context.Matcher; +import com.gitee.dorive.core.api.context.Options; import com.gitee.dorive.core.api.context.Selector; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; @@ -73,8 +74,8 @@ public class CommonRepository extends AbstractProxyRepository implements Matcher } @Override - public boolean matches(Context context) { - return matcher.matches(context); + public boolean matches(Options options) { + return matcher.matches(options); } @Override diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java index 083df183..8217b8df 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java @@ -17,6 +17,7 @@ package com.gitee.dorive.sql.entity.count; +import com.gitee.dorive.core.api.context.Selector; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -33,6 +34,7 @@ public class CountQuery { private Object query; private boolean distinct = true; + private Selector selector; private List countBy; private List groupBy; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java index 713d6cdf..851c10a4 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java @@ -50,8 +50,8 @@ public class CountQuerier { QueryWrapper queryWrapper = new QueryWrapper(countQuery.getQuery()); repository.resolveQuery(queryContext, queryWrapper); - SegmentBuilder segmentBuilder = new SegmentBuilder(); - SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext); + SegmentBuilder segmentBuilder = new SegmentBuilder(queryContext); + SelectSegment selectSegment = segmentBuilder.buildSegment(context, countQuery.getSelector()); TableSegment tableSegment = selectSegment.getTableSegment(); List args = selectSegment.getArgs(); @@ -76,8 +76,9 @@ public class CountQuerier { } private String buildCountByExp(CountQuery countQuery, SegmentBuilder segmentBuilder, String tableAlias, EntityEle entityEle) { - SegmentInfo segmentInfo = segmentBuilder.getFirstMatched(); - if (segmentInfo != null) { + List segmentInfos = segmentBuilder.getMatchedSegmentInfos(); + if (segmentInfos != null && !segmentInfos.isEmpty()) { + SegmentInfo segmentInfo = segmentInfos.get(0); tableAlias = segmentInfo.getTableAlias(); entityEle = segmentInfo.getEntityEle(); } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java index e3896ff4..de61be10 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/executor/SqlQueryExecutor.java @@ -72,8 +72,8 @@ public class SqlQueryExecutor extends AbstractQueryExecutor { boolean needCount = queryContext.isNeedCount(); Result emptyResult = queryContext.newEmptyResult(); - SegmentBuilder segmentBuilder = new SegmentBuilder(); - SelectSegment selectSegment = segmentBuilder.buildSegment(queryContext); + SegmentBuilder segmentBuilder = new SegmentBuilder(queryContext); + SelectSegment selectSegment = segmentBuilder.buildSegment(context, null); char letter = selectSegment.getLetter(); TableSegment tableSegment = selectSegment.getTableSegment(); List argSegments = selectSegment.getArgSegments(); diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index 87ef5e8e..51dc7033 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -20,6 +20,7 @@ package com.gitee.dorive.sql.impl.segment; import com.gitee.dorive.api.constant.Operator; import com.gitee.dorive.api.entity.element.EntityEle; import com.gitee.dorive.core.api.context.Context; +import com.gitee.dorive.core.api.context.Selector; import com.gitee.dorive.core.entity.common.EntityStoreInfo; import com.gitee.dorive.core.entity.executor.Criterion; import com.gitee.dorive.core.entity.executor.Example; @@ -34,31 +35,28 @@ import com.gitee.dorive.query.entity.MergedRepository; import com.gitee.dorive.query.entity.QueryContext; import com.gitee.dorive.query.impl.resolver.QueryResolver; import com.gitee.dorive.sql.entity.context.SegmentInfo; -import com.gitee.dorive.sql.entity.segment.ArgSegment; -import com.gitee.dorive.sql.entity.segment.JoinSegment; -import com.gitee.dorive.sql.entity.segment.OnSegment; -import com.gitee.dorive.sql.entity.segment.SelectSegment; -import com.gitee.dorive.sql.entity.segment.TableSegment; +import com.gitee.dorive.sql.entity.segment.*; import lombok.AllArgsConstructor; import lombok.Data; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Data public class SegmentBuilder { - private SegmentInfo firstMatched; - private List allMatched = new ArrayList<>(8); + private QueryResolver queryResolver; + private Map exampleMap; + private List matchedSegmentInfos; - public SelectSegment buildSegment(QueryContext queryContext) { - Context context = queryContext.getContext(); - QueryResolver queryResolver = queryContext.getQueryResolver(); - Map exampleMap = queryContext.getExampleMap(); + public SegmentBuilder(QueryContext queryContext) { + this.queryResolver = queryContext.getQueryResolver(); + this.exampleMap = queryContext.getExampleMap(); + } + public SelectSegment buildSegment(Context context, Selector selector) { + if (selector != null) { + this.matchedSegmentInfos = new ArrayList<>(8); + } List mergedRepositories = queryResolver.getMergedRepositories(); Map nodeMap = new LinkedHashMap<>(mergedRepositories.size() * 4 / 3 + 1); SelectSegment selectSegment = new SelectSegment(); @@ -78,12 +76,9 @@ public class SegmentBuilder { String tableAlias = selectSegment.generateTableAlias(); SegmentInfo segmentInfo = new SegmentInfo(tableAlias, entityEle); - boolean isMatch = definedRepository.matches(context); + boolean isMatch = selector != null && definedRepository.matches(selector); if (isMatch) { - if (firstMatched == null) { - firstMatched = segmentInfo; - } - allMatched.add(segmentInfo); + matchedSegmentInfos.add(segmentInfo); } Example example = exampleMap.computeIfAbsent(absoluteAccessPath, key -> new InnerExample(Collections.emptyList())); -- Gitee From 82c94d2d6f847442fb4a0c2f01a59a9359266a25 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 12 Mar 2024 10:49:08 +0800 Subject: [PATCH 18/54] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/sql/entity/{count => common}/CountQuery.java | 2 +- .../dorive/sql/entity/{context => common}/SegmentInfo.java | 2 +- .../java/com/gitee/dorive/sql/impl/count/CountQuerier.java | 4 ++-- .../com/gitee/dorive/sql/impl/segment/SegmentBuilder.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename dorive-sql/src/main/java/com/gitee/dorive/sql/entity/{count => common}/CountQuery.java (97%) rename dorive-sql/src/main/java/com/gitee/dorive/sql/entity/{context => common}/SegmentInfo.java (95%) diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/common/CountQuery.java similarity index 97% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/entity/common/CountQuery.java index 8217b8df..20a5c468 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/count/CountQuery.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/common/CountQuery.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.entity.count; +package com.gitee.dorive.sql.entity.common; import com.gitee.dorive.core.api.context.Selector; import lombok.AllArgsConstructor; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/common/SegmentInfo.java similarity index 95% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/entity/common/SegmentInfo.java index bd830890..93638db3 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/context/SegmentInfo.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/common/SegmentInfo.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.sql.entity.context; +package com.gitee.dorive.sql.entity.common; import com.gitee.dorive.api.entity.element.EntityEle; import lombok.AllArgsConstructor; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java index 851c10a4..2fbab183 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java @@ -25,8 +25,8 @@ import com.gitee.dorive.query.entity.QueryWrapper; import com.gitee.dorive.query.entity.enums.ResultType; import com.gitee.dorive.query.repository.AbstractQueryRepository; import com.gitee.dorive.sql.api.SqlRunner; -import com.gitee.dorive.sql.entity.context.SegmentInfo; -import com.gitee.dorive.sql.entity.count.CountQuery; +import com.gitee.dorive.sql.entity.common.SegmentInfo; +import com.gitee.dorive.sql.entity.common.CountQuery; import com.gitee.dorive.sql.entity.segment.SelectSegment; import com.gitee.dorive.sql.entity.segment.TableSegment; import com.gitee.dorive.sql.impl.segment.SegmentBuilder; diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index 51dc7033..a493afe1 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -34,7 +34,7 @@ import com.gitee.dorive.core.util.CriterionUtils; import com.gitee.dorive.query.entity.MergedRepository; import com.gitee.dorive.query.entity.QueryContext; import com.gitee.dorive.query.impl.resolver.QueryResolver; -import com.gitee.dorive.sql.entity.context.SegmentInfo; +import com.gitee.dorive.sql.entity.common.SegmentInfo; import com.gitee.dorive.sql.entity.segment.*; import lombok.AllArgsConstructor; import lombok.Data; -- Gitee From 547a80b5242ed0a40b7f1ea12c6a295422e32773 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 12 Mar 2024 11:32:16 +0800 Subject: [PATCH 19/54] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91?= =?UTF-8?q?=E5=B0=86CountQuerier=E7=BA=B3=E5=85=A5=E6=A0=87=E5=87=86?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/MybatisPlusRepository.java | 15 +++++++--- .../gitee/dorive/sql/api/CountQuerier.java | 29 +++++++++++++++++++ ...tQuerier.java => DefaultCountQuerier.java} | 4 ++- 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 dorive-sql/src/main/java/com/gitee/dorive/sql/api/CountQuerier.java rename dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/{CountQuerier.java => DefaultCountQuerier.java} (97%) diff --git a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java index 79edb74f..b4d9f5d5 100644 --- a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java +++ b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/repository/MybatisPlusRepository.java @@ -34,8 +34,10 @@ import com.gitee.dorive.query.api.QueryExecutor; import com.gitee.dorive.query.entity.QueryContext; import com.gitee.dorive.query.entity.QueryWrapper; import com.gitee.dorive.ref.repository.AbstractRefRepository; +import com.gitee.dorive.sql.api.CountQuerier; import com.gitee.dorive.sql.api.SqlRunner; -import com.gitee.dorive.sql.impl.count.CountQuerier; +import com.gitee.dorive.sql.entity.common.CountQuery; +import com.gitee.dorive.sql.impl.count.DefaultCountQuerier; import com.gitee.dorive.sql.impl.executor.SqlQueryExecutor; import com.gitee.dorive.sql.impl.executor.UnionExecutor; import lombok.Data; @@ -51,18 +53,18 @@ import java.util.Map; @Data @EqualsAndHashCode(callSuper = false) -public class MybatisPlusRepository extends AbstractRefRepository { +public class MybatisPlusRepository extends AbstractRefRepository implements CountQuerier { private SqlRunner sqlRunner; private QueryExecutor sqlQueryExecutor; - private CountQuerier countQuerier; + private CountQuerier defaultCountQuerier; @Override public void afterPropertiesSet() throws Exception { ImplFactory implFactory = getApplicationContext().getBean(ImplFactory.class); this.sqlRunner = implFactory.getInstance(SqlRunner.class); this.sqlQueryExecutor = new SqlQueryExecutor(this, this.sqlRunner); - this.countQuerier = new CountQuerier(this, this.sqlRunner); + this.defaultCountQuerier = new DefaultCountQuerier(this, this.sqlRunner); super.afterPropertiesSet(); } @@ -133,4 +135,9 @@ public class MybatisPlusRepository extends AbstractRefRepository { return super.adaptiveQueryExecutor(queryContext, queryWrapper); } + @Override + public Map selectCountMap(Context context, CountQuery countQuery) { + return defaultCountQuerier.selectCountMap(context, countQuery); + } + } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/api/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/api/CountQuerier.java new file mode 100644 index 00000000..9317f540 --- /dev/null +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/api/CountQuerier.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.sql.api; + +import com.gitee.dorive.core.api.context.Context; +import com.gitee.dorive.sql.entity.common.CountQuery; + +import java.util.Map; + +public interface CountQuerier { + + Map selectCountMap(Context context, CountQuery countQuery); + +} diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/DefaultCountQuerier.java similarity index 97% rename from dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java rename to dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/DefaultCountQuerier.java index 2fbab183..545b0284 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/CountQuerier.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/count/DefaultCountQuerier.java @@ -24,6 +24,7 @@ import com.gitee.dorive.query.entity.QueryContext; import com.gitee.dorive.query.entity.QueryWrapper; import com.gitee.dorive.query.entity.enums.ResultType; import com.gitee.dorive.query.repository.AbstractQueryRepository; +import com.gitee.dorive.sql.api.CountQuerier; import com.gitee.dorive.sql.api.SqlRunner; import com.gitee.dorive.sql.entity.common.SegmentInfo; import com.gitee.dorive.sql.entity.common.CountQuery; @@ -40,11 +41,12 @@ import java.util.Map; @Data @AllArgsConstructor -public class CountQuerier { +public class DefaultCountQuerier implements CountQuerier { private AbstractQueryRepository repository; private SqlRunner sqlRunner; + @Override public Map selectCountMap(Context context, CountQuery countQuery) { QueryContext queryContext = new QueryContext(context, ResultType.COUNT); QueryWrapper queryWrapper = new QueryWrapper(countQuery.getQuery()); -- Gitee From 681bb7e88196cdc189b8e7e6667f266b81888ece Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 14 Mar 2024 11:12:14 +0800 Subject: [PATCH 20/54] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8D=E3=80=91?= =?UTF-8?q?=E6=97=A0=E6=A0=B9=E9=80=89=E5=8F=96=E6=97=B6=EF=BC=8C=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E8=BF=94=E5=9B=9E=E7=A9=BA=E5=88=86=E9=A1=B5=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E9=80=A0=E6=88=90=E7=A9=BA=E6=8C=87=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/core/entity/executor/Result.java | 12 ++++++++++++ .../dorive/core/impl/executor/ContextExecutor.java | 2 +- .../query/repository/AbstractQueryRepository.java | 5 +++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/entity/executor/Result.java b/dorive-core/src/main/java/com/gitee/dorive/core/entity/executor/Result.java index c92bbb2d..1b35bcd8 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/entity/executor/Result.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/entity/executor/Result.java @@ -17,6 +17,7 @@ package com.gitee.dorive.core.entity.executor; +import com.gitee.dorive.core.entity.operation.Query; import lombok.Data; import lombok.NoArgsConstructor; @@ -34,6 +35,17 @@ public class Result { private E record; private long count = 0L; + public static Result emptyResult(Query query) { + Example example = query.getExample(); + if (example != null) { + Page page = example.getPage(); + if (page != null) { + return new Result<>(page); + } + } + return new Result<>(); + } + public Result(Page page, List> recordMaps) { this.page = page; this.recordMaps = recordMaps; diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java index a9dd8c0e..858e282c 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/executor/ContextExecutor.java @@ -66,7 +66,7 @@ public class ContextExecutor extends AbstractExecutor { } return result; } - return new Result<>(); + return Result.emptyResult(query); } public void populate(Context context, List entities) { diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java b/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java index 210ac4c9..ecea1862 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java @@ -23,6 +23,7 @@ import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.api.context.Options; import com.gitee.dorive.core.entity.executor.Page; import com.gitee.dorive.core.entity.executor.Result; +import com.gitee.dorive.core.repository.CommonRepository; import com.gitee.dorive.event.repository.AbstractEventRepository; import com.gitee.dorive.query.api.QueryExecutor; import com.gitee.dorive.query.api.QueryRepository; @@ -111,6 +112,10 @@ public abstract class AbstractQueryRepository extends AbstractEventReposi @Override public Result executeQuery(QueryContext queryContext, QueryWrapper queryWrapper) { resolveQuery(queryContext, queryWrapper); + CommonRepository rootRepository = getRootRepository(); + if (!rootRepository.matches(queryContext.getContext())) { + return queryContext.newEmptyResult(); + } if (queryContext.isSimpleQuery()) { return simpleQueryExecutor.executeQuery(queryContext, queryWrapper); } else { -- Gitee From d81f0cecd5cfb677b9f1dfe9ee22b8dcc0e1e8d3 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 14 Mar 2024 11:13:24 +0800 Subject: [PATCH 21/54] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8D=E3=80=91?= =?UTF-8?q?=E6=97=A0=E6=A0=B9=E9=80=89=E5=8F=96=E6=97=B6=EF=BC=8C=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E8=BF=94=E5=9B=9E=E7=A9=BA=E5=88=86=E9=A1=B5=EF=BC=8C?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E9=80=A0=E6=88=90=E7=A9=BA=E6=8C=87=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dorive/query/repository/AbstractQueryRepository.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java b/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java index ecea1862..ea1784a8 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/repository/AbstractQueryRepository.java @@ -20,10 +20,10 @@ package com.gitee.dorive.query.repository; import cn.hutool.core.lang.Assert; import com.gitee.dorive.api.annotation.Repository; import com.gitee.dorive.core.api.context.Context; +import com.gitee.dorive.core.api.context.Matcher; import com.gitee.dorive.core.api.context.Options; import com.gitee.dorive.core.entity.executor.Page; import com.gitee.dorive.core.entity.executor.Result; -import com.gitee.dorive.core.repository.CommonRepository; import com.gitee.dorive.event.repository.AbstractEventRepository; import com.gitee.dorive.query.api.QueryExecutor; import com.gitee.dorive.query.api.QueryRepository; @@ -112,8 +112,8 @@ public abstract class AbstractQueryRepository extends AbstractEventReposi @Override public Result executeQuery(QueryContext queryContext, QueryWrapper queryWrapper) { resolveQuery(queryContext, queryWrapper); - CommonRepository rootRepository = getRootRepository(); - if (!rootRepository.matches(queryContext.getContext())) { + Matcher matcher = getRootRepository(); + if (!matcher.matches(queryContext.getContext())) { return queryContext.newEmptyResult(); } if (queryContext.isSimpleQuery()) { -- Gitee From bdd6d1270d543f06972b789a5beb572a928269cd Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 14 Mar 2024 11:53:18 +0800 Subject: [PATCH 22/54] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8D=E3=80=91?= =?UTF-8?q?=E5=A4=9A=E5=AD=97=E6=AE=B5=E8=81=94=E6=9F=A5=E5=92=8C=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E6=8E=92=E5=BA=8F=EF=BC=8C=E9=80=A0=E6=88=90=E6=8B=BC?= =?UTF-8?q?=E6=8E=A5sql=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mybatis/plus/api/CriterionAppender.java | 3 +- .../mybatis/plus/impl/AppenderContext.java | 73 +++++++++++-------- 2 files changed, 46 insertions(+), 30 deletions(-) diff --git a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/api/CriterionAppender.java b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/api/CriterionAppender.java index 6ec4c597..81ed3bd9 100644 --- a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/api/CriterionAppender.java +++ b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/api/CriterionAppender.java @@ -18,9 +18,10 @@ package com.gitee.dorive.mybatis.plus.api; import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; +import com.gitee.dorive.core.entity.executor.Example; public interface CriterionAppender { - void appendCriterion(AbstractWrapper abstractWrapper, int criteriaNum, String property, Object value); + void appendCriterion(AbstractWrapper wrapper, Example example, String property, Object value); } diff --git a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/impl/AppenderContext.java b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/impl/AppenderContext.java index 4d64f818..178afcc8 100644 --- a/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/impl/AppenderContext.java +++ b/dorive-mybatis-plus/src/main/java/com/gitee/dorive/mybatis/plus/impl/AppenderContext.java @@ -18,9 +18,11 @@ package com.gitee.dorive.mybatis.plus.impl; import com.baomidou.mybatisplus.core.conditions.AbstractWrapper; +import com.baomidou.mybatisplus.core.toolkit.StringPool; import com.gitee.dorive.api.constant.Operator; import com.gitee.dorive.core.entity.executor.Criterion; import com.gitee.dorive.core.entity.executor.Example; +import com.gitee.dorive.core.entity.executor.OrderBy; import com.gitee.dorive.mybatis.plus.api.CriterionAppender; import java.util.Collection; @@ -33,56 +35,69 @@ public class AppenderContext { public final static Map OPERATOR_CRITERION_APPENDER_MAP = new ConcurrentHashMap<>(); static { - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.EQ, (abstractWrapper, criteriaNum, property, value) -> { + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.EQ, (wrapper, example, property, value) -> { if (value instanceof Collection) { - abstractWrapper.in(property, (Collection) value); + wrapper.in(property, (Collection) value); } else { - abstractWrapper.eq(property, value); + wrapper.eq(property, value); } }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NE, (abstractWrapper, criteriaNum, property, value) -> { + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NE, (wrapper, example, property, value) -> { if (value instanceof Collection) { - abstractWrapper.notIn(property, (Collection) value); + wrapper.notIn(property, (Collection) value); } else { - abstractWrapper.ne(property, value); + wrapper.ne(property, value); } }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.GT, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.gt(property, value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.GE, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.ge(property, value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LT, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.lt(property, value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LE, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.le(property, value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IN, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.in(property, (Collection) value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NOT_IN, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.notIn(property, (Collection) value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LIKE, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.like(property, value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NOT_LIKE, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.notLike(property, value)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IS_NULL, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.isNull(property)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IS_NOT_NULL, (abstractWrapper, criteriaNum, property, value) -> abstractWrapper.isNotNull(property)); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.MULTI_IN, (abstractWrapper, criteriaNum, property, value) -> { - String prefix = criteriaNum == 1 ? " WHERE " : " AND "; - abstractWrapper.last(prefix + "(" + property + ") IN (" + value + ")"); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.GT, (wrapper, example, property, value) -> wrapper.gt(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.GE, (wrapper, example, property, value) -> wrapper.ge(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LT, (wrapper, example, property, value) -> wrapper.lt(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LE, (wrapper, example, property, value) -> wrapper.le(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IN, (wrapper, example, property, value) -> wrapper.in(property, (Collection) value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NOT_IN, (wrapper, example, property, value) -> wrapper.notIn(property, (Collection) value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.LIKE, (wrapper, example, property, value) -> wrapper.like(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.NOT_LIKE, (wrapper, example, property, value) -> wrapper.notLike(property, value)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IS_NULL, (wrapper, example, property, value) -> wrapper.isNull(property)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.IS_NOT_NULL, (wrapper, example, property, value) -> wrapper.isNotNull(property)); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.MULTI_IN, (wrapper, example, property, value) -> { + List criteria = example.getCriteria(); + OrderBy orderBy = example.getOrderBy(); + String prefix = criteria.size() == 1 ? " WHERE " : " AND "; + String lastSql = prefix + "(" + property + ") IN (" + value + ")"; + if (orderBy != null) { + lastSql = lastSql + StringPool.SPACE + orderBy; + example.setOrderBy(null); + } + wrapper.last(lastSql); }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.MULTI_NOT_IN, (abstractWrapper, criteriaNum, property, value) -> { - String prefix = criteriaNum == 1 ? " WHERE " : " AND "; - abstractWrapper.last(prefix + "(" + property + ") NOT IN (" + value + ")"); + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.MULTI_NOT_IN, (wrapper, example, property, value) -> { + List criteria = example.getCriteria(); + OrderBy orderBy = example.getOrderBy(); + String prefix = criteria.size() == 1 ? " WHERE " : " AND "; + String lastSql = prefix + "(" + property + ") NOT IN (" + value + ")"; + if (orderBy != null) { + lastSql = lastSql + StringPool.SPACE + orderBy; + example.setOrderBy(null); + } + wrapper.last(lastSql); }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.AND, (abstractWrapper, criteriaNum, property, value) -> { + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.AND, (wrapper, example, property, value) -> { if (value instanceof Example) { - abstractWrapper.and(q -> appendCriterion(q, (Example) value)); + wrapper.and(q -> appendCriterion(q, (Example) value)); } }); - OPERATOR_CRITERION_APPENDER_MAP.put(Operator.OR, (abstractWrapper, criteriaNum, property, value) -> { + OPERATOR_CRITERION_APPENDER_MAP.put(Operator.OR, (wrapper, example, property, value) -> { if (value instanceof Example) { - abstractWrapper.or(q -> appendCriterion(q, (Example) value)); + wrapper.or(q -> appendCriterion(q, (Example) value)); } }); } - public static void appendCriterion(AbstractWrapper abstractWrapper, Example example) { + public static void appendCriterion(AbstractWrapper wrapper, Example example) { List criteria = example.getCriteria(); - int criteriaNum = criteria.size(); for (Criterion criterion : criteria) { CriterionAppender criterionAppender = OPERATOR_CRITERION_APPENDER_MAP.get(criterion.getOperator()); - criterionAppender.appendCriterion(abstractWrapper, criteriaNum, criterion.getProperty(), criterion.getValue()); + criterionAppender.appendCriterion(wrapper, example, criterion.getProperty(), criterion.getValue()); } } -- Gitee From 3a7e4a4e88ac2b5d4b986c04603cfc725b5a7f0d Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Fri, 15 Mar 2024 10:13:34 +0800 Subject: [PATCH 23/54] =?UTF-8?q?=E5=B0=81=E8=A3=85=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/entity/operation/Operation.java | 2 +- .../core/entity/option/BindingType.java | 23 ++++++++++++++++ .../core/impl/resolver/BinderResolver.java | 26 ++++++++++++++----- .../repository/AbstractGenericRepository.java | 4 +-- .../web/advice/ParameterControllerAdvice.java | 17 ++++++++++++ 5 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 dorive-core/src/main/java/com/gitee/dorive/core/entity/option/BindingType.java diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/entity/operation/Operation.java b/dorive-core/src/main/java/com/gitee/dorive/core/entity/operation/Operation.java index 350d824c..f7bb315f 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/entity/operation/Operation.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/entity/operation/Operation.java @@ -22,7 +22,7 @@ import lombok.Data; @Data public class Operation { - public enum RootControl {NONE, INCLUDE_ROOT, IGNORE_ROOT,} + public enum RootControl {NONE, INCLUDE_ROOT, IGNORE_ROOT} private RootControl rootControl = RootControl.NONE; private Object entity; diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/entity/option/BindingType.java b/dorive-core/src/main/java/com/gitee/dorive/core/entity/option/BindingType.java new file mode 100644 index 00000000..21de196f --- /dev/null +++ b/dorive-core/src/main/java/com/gitee/dorive/core/entity/option/BindingType.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.core.entity.option; + +public enum BindingType { + STRONG, + WEAK +} diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java index 8f143f9b..2c611b03 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java @@ -20,6 +20,7 @@ package com.gitee.dorive.core.impl.resolver; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import com.gitee.dorive.api.entity.def.BindingDef; @@ -29,6 +30,7 @@ import com.gitee.dorive.api.entity.element.PropChain; import com.gitee.dorive.api.impl.resolver.PropChainResolver; import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.api.binder.Processor; +import com.gitee.dorive.core.entity.option.BindingType; import com.gitee.dorive.core.entity.option.JoinType; import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.impl.binder.SpELProcessor; @@ -82,10 +84,10 @@ public class BinderResolver { String fieldErrorMsg = "The field configured for @Binding does not exist within the entity! type: {}, field: {}"; for (BindingDef bindingDef : bindingDefs) { + BindingType bindingType = determineBindingType(bindingDef); bindingDef = renewBindingDef(accessPath, bindingDef); - String field = bindingDef.getField(); - String bindExp = bindingDef.getBindExp(); + String field = bindingDef.getField(); String alias = entityEle.toAlias(field); PropChain fieldPropChain = propChainMap.get("/" + field); @@ -94,7 +96,7 @@ public class BinderResolver { Processor processor = newProcessor(bindingDef); - if (StringUtils.isNotBlank(bindExp)) { + if (bindingType == BindingType.STRONG) { StrongBinder strongBinder = newStrongBinder(bindingDef, alias, fieldPropChain, processor); allBinders.add(strongBinder); strongBinders.add(strongBinder); @@ -109,10 +111,9 @@ public class BinderResolver { } boundIdBinder = strongBinder; } - selfFields.add(field); - } else { + } else if (bindingType == BindingType.WEAK) { WeakBinder weakBinder = new WeakBinder(bindingDef, alias, fieldPropChain, processor); allBinders.add(weakBinder); weakBinders.add(weakBinder); @@ -130,6 +131,19 @@ public class BinderResolver { } } + private BindingType determineBindingType(BindingDef bindingDef) { + String field = StrUtil.trim(bindingDef.getField()); + String bindExp = StrUtil.trim(bindingDef.getBindExp()); + String processExp = StrUtil.trim(bindingDef.getProcessExp()); + if (ObjectUtil.isAllNotEmpty(field, bindExp)) { + return BindingType.STRONG; + + } else if (ObjectUtil.isAllNotEmpty(field, processExp)) { + return BindingType.WEAK; + } + throw new RuntimeException("Unknown binding type!"); + } + private BindingDef renewBindingDef(String accessPath, BindingDef bindingDef) { bindingDef = BeanUtil.copyProperties(bindingDef, BindingDef.class); String field = StrUtil.trim(bindingDef.getField()); @@ -137,8 +151,6 @@ public class BinderResolver { String processExp = StrUtil.trim(bindingDef.getProcessExp()); String bindField = StrUtil.trim(bindingDef.getBindField()); Class processor = bindingDef.getProcessor(); - Assert.notEmpty(field, "The field of @Binding cannot be empty!"); - Assert.notEmpty(bindExp + processExp, "The expression of @Binding cannot be empty!"); if (bindExp.startsWith(".")) { bindExp = PathUtils.getAbsolutePath(accessPath, bindExp); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/repository/AbstractGenericRepository.java b/dorive-core/src/main/java/com/gitee/dorive/core/repository/AbstractGenericRepository.java index a8480cb4..dc12acfc 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/repository/AbstractGenericRepository.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/repository/AbstractGenericRepository.java @@ -43,7 +43,7 @@ public abstract class AbstractGenericRepository extends AbstractContextRe Assert.notNull(example, "The example cannot be null!"); int totalCount = 0; for (CommonRepository repository : getOrderedRepositories()) { - if (repository.matches((Context) options)) { + if (repository.matches(options)) { totalCount += repository.updateByExample(options, entity, ExampleUtils.clone(example)); } } @@ -69,7 +69,7 @@ public abstract class AbstractGenericRepository extends AbstractContextRe Assert.notNull(example, "The example cannot be null!"); int totalCount = 0; for (CommonRepository repository : getOrderedRepositories()) { - if (repository.matches((Context) options)) { + if (repository.matches(options)) { totalCount += repository.deleteByExample(options, ExampleUtils.clone(example)); } } diff --git a/dorive-web/src/main/java/com/gitee/dorive/web/advice/ParameterControllerAdvice.java b/dorive-web/src/main/java/com/gitee/dorive/web/advice/ParameterControllerAdvice.java index db8e69e9..4720d255 100644 --- a/dorive-web/src/main/java/com/gitee/dorive/web/advice/ParameterControllerAdvice.java +++ b/dorive-web/src/main/java/com/gitee/dorive/web/advice/ParameterControllerAdvice.java @@ -1,3 +1,20 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.web.advice; import cn.hutool.core.date.DateUtil; -- Gitee From 7f89862861664324a3ab3b9f88e12ecc783dcfb5 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Fri, 15 Mar 2024 18:52:14 +0800 Subject: [PATCH 24/54] =?UTF-8?q?=E3=80=90=E9=87=8D=E8=A6=81=E3=80=91?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=AF=B9=E5=80=BC=E7=BB=91=E5=AE=9A=E7=9A=84?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/api/annotation/Binding.java | 4 +- .../dorive/api/entity/def/BindingDef.java | 1 + .../gitee/dorive/core/api/binder/Binder.java | 2 +- .../core/entity/option/BindingType.java | 3 +- .../dorive/core/impl/binder/BoundBinder.java | 85 +++++++++++++++++++ .../{AbstractBinder.java => FieldBinder.java} | 31 ++++++- .../dorive/core/impl/binder/StrongBinder.java | 37 ++------ .../dorive/core/impl/binder/ValueBinder.java | 39 +++++++++ .../dorive/core/impl/binder/WeakBinder.java | 21 +---- .../core/impl/handler/BatchEntityHandler.java | 29 +++++++ .../core/impl/joiner/MultiEntityJoiner.java | 4 +- .../{binder => processor}/SpELProcessor.java | 2 +- .../core/impl/resolver/BinderResolver.java | 38 +++++++-- .../dorive/query/entity/MergedRepository.java | 4 +- .../impl/executor/StepwiseQueryExecutor.java | 27 ++++-- .../resolver/MergedRepositoryResolver.java | 18 +++- .../dorive/sql/entity/segment/OnSegment.java | 7 +- .../sql/impl/segment/SegmentBuilder.java | 30 +++++-- 18 files changed, 298 insertions(+), 84 deletions(-) create mode 100644 dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java rename dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/{AbstractBinder.java => FieldBinder.java} (71%) create mode 100644 dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java rename dorive-core/src/main/java/com/gitee/dorive/core/impl/{binder => processor}/SpELProcessor.java (97%) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java index 52f0f217..a075c775 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java @@ -26,7 +26,9 @@ import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.FIELD}) public @interface Binding { - String field(); + String field() default ""; + + String value() default ""; String bindExp() default ""; diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java b/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java index 5e3ad9a8..c4678188 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java @@ -37,6 +37,7 @@ import java.util.Set; public class BindingDef { private String field; + private String value; private String bindExp; private String processExp; private String bindField; diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java b/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java index ffe6d88f..02ac3f00 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/api/binder/Binder.java @@ -19,7 +19,7 @@ package com.gitee.dorive.core.api.binder; import com.gitee.dorive.core.api.context.Context; -public interface Binder { +public interface Binder extends Processor { String getFieldName(); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/entity/option/BindingType.java b/dorive-core/src/main/java/com/gitee/dorive/core/entity/option/BindingType.java index 21de196f..89ea6fc6 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/entity/option/BindingType.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/entity/option/BindingType.java @@ -19,5 +19,6 @@ package com.gitee.dorive.core.entity.option; public enum BindingType { STRONG, - WEAK + WEAK, + VALUE } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java new file mode 100644 index 00000000..f06655e8 --- /dev/null +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java @@ -0,0 +1,85 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.core.impl.binder; + +import com.gitee.dorive.api.entity.def.BindingDef; +import com.gitee.dorive.api.entity.element.PropChain; +import com.gitee.dorive.core.api.binder.Binder; +import com.gitee.dorive.core.api.binder.Processor; +import com.gitee.dorive.core.api.context.Context; +import com.gitee.dorive.core.repository.CommonRepository; +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class BoundBinder implements Binder { + + protected BindingDef bindingDef; + protected Processor processor; + private String belongAccessPath; + private CommonRepository belongRepository; + private PropChain boundPropChain; + private String bindAlias; + + public BoundBinder(BindingDef bindingDef, Processor processor) { + this.bindingDef = bindingDef; + this.processor = processor; + } + + @Override + public String getFieldName() { + return null; + } + + @Override + public Object getFieldValue(Context context, Object entity) { + return null; + } + + @Override + public void setFieldValue(Context context, Object entity, Object property) { + // ignore + } + + @Override + public String getBoundName() { + return boundPropChain.getEntityField().getName(); + } + + @Override + public Object getBoundValue(Context context, Object rootEntity) { + return boundPropChain.getValue(rootEntity); + } + + @Override + public void setBoundValue(Context context, Object rootEntity, Object property) { + boundPropChain.setValue(rootEntity, property); + } + + @Override + public Object input(Context context, Object value) { + return value == null || processor == null ? value : processor.input(context, value); + } + + @Override + public Object output(Context context, Object value) { + return value == null || processor == null ? value : processor.output(context, value); + } + +} diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/FieldBinder.java similarity index 71% rename from dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java rename to dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/FieldBinder.java index cf3b6c08..60df494f 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/AbstractBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/FieldBinder.java @@ -27,12 +27,12 @@ import lombok.Data; @Data @AllArgsConstructor -public abstract class AbstractBinder implements Binder, Processor { +public class FieldBinder implements Binder { protected BindingDef bindingDef; - protected String alias; - protected PropChain fieldPropChain; protected Processor processor; + protected PropChain fieldPropChain; + protected String alias; @Override public String getFieldName() { @@ -49,4 +49,29 @@ public abstract class AbstractBinder implements Binder, Processor { fieldPropChain.setValue(entity, property); } + @Override + public String getBoundName() { + return null; + } + + @Override + public Object getBoundValue(Context context, Object rootEntity) { + return null; + } + + @Override + public void setBoundValue(Context context, Object rootEntity, Object property) { + // ignore + } + + @Override + public Object input(Context context, Object value) { + return value == null || processor == null ? value : processor.input(context, value); + } + + @Override + public Object output(Context context, Object value) { + return value == null || processor == null ? value : processor.output(context, value); + } + } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java index 19a9b20d..633c4d20 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java @@ -21,59 +21,40 @@ import com.gitee.dorive.api.entity.def.BindingDef; import com.gitee.dorive.api.entity.element.PropChain; import com.gitee.dorive.core.api.binder.Processor; import com.gitee.dorive.core.api.context.Context; -import com.gitee.dorive.core.repository.CommonRepository; import lombok.Getter; import lombok.Setter; @Getter @Setter -public class StrongBinder extends AbstractBinder { +public class StrongBinder extends FieldBinder { - private String belongAccessPath; - private CommonRepository belongRepository; - private PropChain boundPropChain; - private String bindAlias; + private BoundBinder boundBinder; - public StrongBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor, - String belongAccessPath, CommonRepository belongRepository, PropChain boundPropChain, String bindAlias) { - super(bindingDef, alias, fieldPropChain, processor); - this.belongAccessPath = belongAccessPath; - this.belongRepository = belongRepository; - this.boundPropChain = boundPropChain; - this.bindAlias = bindAlias; + public StrongBinder(BindingDef bindingDef, Processor processor, PropChain fieldPropChain, String alias) { + super(bindingDef, processor, fieldPropChain, alias); } @Override public String getBoundName() { - return boundPropChain.getEntityField().getName(); + return boundBinder.getBoundName(); } @Override public Object getBoundValue(Context context, Object rootEntity) { - return boundPropChain.getValue(rootEntity); + return boundBinder.getBoundValue(context, rootEntity); } @Override public void setBoundValue(Context context, Object rootEntity, Object property) { - boundPropChain.setValue(rootEntity, property); - } - - @Override - public Object input(Context context, Object value) { - return value == null || processor == null ? value : processor.input(context, value); - } - - @Override - public Object output(Context context, Object value) { - return value == null || processor == null ? value : processor.output(context, value); + boundBinder.setBoundValue(context, rootEntity, property); } public boolean isSameType() { - return getFieldPropChain().isSameType(boundPropChain); + return getFieldPropChain().isSameType(boundBinder.getBoundPropChain()); } public boolean isCollection() { - return boundPropChain.getEntityField().isCollection(); + return boundBinder.getBoundPropChain().getEntityField().isCollection(); } } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java new file mode 100644 index 00000000..b9a9653d --- /dev/null +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 com.gitee.dorive.core.impl.binder; + +import com.gitee.dorive.api.entity.def.BindingDef; +import com.gitee.dorive.core.api.binder.Processor; +import com.gitee.dorive.core.api.context.Context; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ValueBinder extends BoundBinder { + + public ValueBinder(BindingDef bindingDef, Processor processor) { + super(bindingDef, processor); + } + + @Override + public Object getFieldValue(Context context, Object entity) { + return bindingDef.getValue(); + } + +} diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/WeakBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/WeakBinder.java index eec63275..e228a158 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/WeakBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/WeakBinder.java @@ -22,25 +22,10 @@ import com.gitee.dorive.api.entity.element.PropChain; import com.gitee.dorive.core.api.binder.Processor; import com.gitee.dorive.core.api.context.Context; -public class WeakBinder extends AbstractBinder { +public class WeakBinder extends FieldBinder { - public WeakBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { - super(bindingDef, alias, fieldPropChain, processor); - } - - @Override - public String getBoundName() { - return null; - } - - @Override - public Object getBoundValue(Context context, Object rootEntity) { - return null; - } - - @Override - public void setBoundValue(Context context, Object rootEntity, Object property) { - // ignore + public WeakBinder(BindingDef bindingDef, Processor processor, PropChain fieldPropChain, String alias) { + super(bindingDef, processor, fieldPropChain, alias); } @Override diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java index 1c196901..58d2e3af 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java @@ -24,15 +24,18 @@ import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.Result; import com.gitee.dorive.core.entity.operation.Query; import com.gitee.dorive.core.entity.option.JoinType; +import com.gitee.dorive.core.impl.binder.ValueBinder; import com.gitee.dorive.core.impl.factory.OperationFactory; import com.gitee.dorive.core.impl.joiner.MultiEntityJoiner; import com.gitee.dorive.core.impl.joiner.SingleEntityJoiner; import com.gitee.dorive.core.impl.joiner.UnionEntityJoiner; +import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.AbstractContextRepository; import com.gitee.dorive.core.repository.CommonRepository; import lombok.AllArgsConstructor; import lombok.Data; +import java.util.ArrayList; import java.util.List; @Data @@ -46,6 +49,7 @@ public class BatchEntityHandler implements EntityHandler { long totalCount = 0L; for (CommonRepository repository : this.repository.getSubRepositories()) { if (repository.matches(context)) { + entities = filterByValueBinders(context, entities, repository); EntityJoiner entityJoiner = newEntityJoiner(repository, entities.size()); if (entityJoiner == null) { continue; @@ -65,6 +69,31 @@ public class BatchEntityHandler implements EntityHandler { return totalCount; } + private List filterByValueBinders(Context context, List entities, CommonRepository repository) { + BinderResolver binderResolver = repository.getBinderResolver(); + List valueBinders = binderResolver.getValueBinders(); + if (valueBinders.isEmpty()) { + return entities; + } + List newEntities = new ArrayList<>(entities.size()); + for (Object entity : entities) { + boolean flag = true; + for (ValueBinder valueBinder : valueBinders) { + Object fieldValue = valueBinder.getFieldValue(context, null); + Object boundValue = valueBinder.getBoundValue(context, entity); + boundValue = valueBinder.input(context, boundValue); + if (boundValue == null || !fieldValue.equals(boundValue.toString())) { + flag = false; + break; + } + } + if (flag) { + newEntities.add(entity); + } + } + return newEntities; + } + protected EntityJoiner newEntityJoiner(CommonRepository repository, int entitiesSize) { JoinType joinType = repository.getJoinType(); if (joinType == JoinType.SINGLE) { diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java index c1f58f40..039a5a58 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/joiner/MultiEntityJoiner.java @@ -21,7 +21,7 @@ import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; import com.gitee.dorive.core.entity.executor.Result; -import com.gitee.dorive.core.impl.binder.AbstractBinder; +import com.gitee.dorive.core.impl.binder.FieldBinder; import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.repository.CommonRepository; import com.gitee.dorive.core.util.MultiInBuilder; @@ -54,7 +54,7 @@ public class MultiEntityJoiner extends AbstractEntityJoiner { } private MultiInBuilder newMultiInBuilder(Context context, List entities) { - List aliases = binders.stream().map(AbstractBinder::getAlias).collect(Collectors.toList()); + List aliases = binders.stream().map(FieldBinder::getAlias).collect(Collectors.toList()); MultiInBuilder multiInBuilder = new MultiInBuilder(aliases, entities.size()); for (Object entity : entities) { diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/SpELProcessor.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/SpELProcessor.java similarity index 97% rename from dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/SpELProcessor.java rename to dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/SpELProcessor.java index 6be5edbb..b9723d72 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/SpELProcessor.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/processor/SpELProcessor.java @@ -15,7 +15,7 @@ * limitations under the License. */ -package com.gitee.dorive.core.impl.binder; +package com.gitee.dorive.core.impl.processor; import com.gitee.dorive.api.entity.def.BindingDef; import com.gitee.dorive.core.api.binder.Processor; diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java index 2c611b03..930bb418 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java @@ -32,9 +32,11 @@ import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.api.binder.Processor; import com.gitee.dorive.core.entity.option.BindingType; import com.gitee.dorive.core.entity.option.JoinType; +import com.gitee.dorive.core.impl.binder.BoundBinder; import com.gitee.dorive.core.impl.binder.StrongBinder; -import com.gitee.dorive.core.impl.binder.SpELProcessor; +import com.gitee.dorive.core.impl.binder.ValueBinder; import com.gitee.dorive.core.impl.binder.WeakBinder; +import com.gitee.dorive.core.impl.processor.SpELProcessor; import com.gitee.dorive.core.repository.AbstractContextRepository; import com.gitee.dorive.core.repository.CommonRepository; import com.gitee.dorive.core.util.PathUtils; @@ -56,11 +58,13 @@ public class BinderResolver { private List allBinders; private List strongBinders; + // 决定了关联查询具体使用哪种实现 private Map> mergedBindersMap; private StrongBinder boundIdBinder; private List selfFields; private JoinType joinType; private List weakBinders; + private List valueBinders; public BinderResolver(AbstractContextRepository repository, EntityEle entityEle) { this.repository = repository; @@ -81,11 +85,21 @@ public class BinderResolver { this.selfFields = new ArrayList<>(bindingDefs.size()); this.joinType = JoinType.UNION; this.weakBinders = new ArrayList<>(bindingDefs.size()); + this.valueBinders = new ArrayList<>(bindingDefs.size()); String fieldErrorMsg = "The field configured for @Binding does not exist within the entity! type: {}, field: {}"; for (BindingDef bindingDef : bindingDefs) { BindingType bindingType = determineBindingType(bindingDef); bindingDef = renewBindingDef(accessPath, bindingDef); + Processor processor = newProcessor(bindingDef); + + if (bindingType == BindingType.VALUE) { + ValueBinder valueBinder = new ValueBinder(bindingDef, processor); + initBoundBinder(bindingDef, valueBinder); + allBinders.add(valueBinder); + valueBinders.add(valueBinder); + continue; + } String field = bindingDef.getField(); String alias = entityEle.toAlias(field); @@ -94,14 +108,14 @@ public class BinderResolver { Assert.notNull(fieldPropChain, fieldErrorMsg, genericType.getName(), field); fieldPropChain.newPropProxy(); - Processor processor = newProcessor(bindingDef); - if (bindingType == BindingType.STRONG) { - StrongBinder strongBinder = newStrongBinder(bindingDef, alias, fieldPropChain, processor); + StrongBinder strongBinder = new StrongBinder(bindingDef, processor, fieldPropChain, alias); + BoundBinder boundBinder = new BoundBinder(bindingDef, processor); + initBoundBinder(bindingDef, boundBinder); allBinders.add(strongBinder); strongBinders.add(strongBinder); - String belongAccessPath = strongBinder.getBelongAccessPath(); + String belongAccessPath = boundBinder.getBelongAccessPath(); List strongBinders = mergedBindersMap.computeIfAbsent(belongAccessPath, key -> new ArrayList<>(2)); strongBinders.add(strongBinder); @@ -114,7 +128,7 @@ public class BinderResolver { selfFields.add(field); } else if (bindingType == BindingType.WEAK) { - WeakBinder weakBinder = new WeakBinder(bindingDef, alias, fieldPropChain, processor); + WeakBinder weakBinder = new WeakBinder(bindingDef, processor, fieldPropChain, alias); allBinders.add(weakBinder); weakBinders.add(weakBinder); } @@ -133,6 +147,7 @@ public class BinderResolver { private BindingType determineBindingType(BindingDef bindingDef) { String field = StrUtil.trim(bindingDef.getField()); + String value = StrUtil.trim(bindingDef.getValue()); String bindExp = StrUtil.trim(bindingDef.getBindExp()); String processExp = StrUtil.trim(bindingDef.getProcessExp()); if (ObjectUtil.isAllNotEmpty(field, bindExp)) { @@ -140,6 +155,9 @@ public class BinderResolver { } else if (ObjectUtil.isAllNotEmpty(field, processExp)) { return BindingType.WEAK; + + } else if (ObjectUtil.isAllNotEmpty(value, bindExp)) { + return BindingType.VALUE; } throw new RuntimeException("Unknown binding type!"); } @@ -193,7 +211,7 @@ public class BinderResolver { } } - private StrongBinder newStrongBinder(BindingDef bindingDef, String alias, PropChain fieldPropChain, Processor processor) { + private void initBoundBinder(BindingDef bindingDef, BoundBinder boundBinder) { String bindExp = bindingDef.getBindExp(); String bindField = bindingDef.getBindField(); @@ -212,8 +230,10 @@ public class BinderResolver { EntityEle entityEle = belongRepository.getEntityEle(); String bindAlias = entityEle.toAlias(bindField); - return new StrongBinder(bindingDef, alias, fieldPropChain, processor, - belongAccessPath, belongRepository, boundPropChain, bindAlias); + boundBinder.setBelongAccessPath(belongAccessPath); + boundBinder.setBelongRepository(belongRepository); + boundBinder.setBoundPropChain(boundPropChain); + boundBinder.setBindAlias(bindAlias); } } diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java b/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java index fcbe1e96..113ccede 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java @@ -17,7 +17,7 @@ package com.gitee.dorive.query.entity; -import com.gitee.dorive.core.impl.binder.StrongBinder; +import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.repository.CommonRepository; import lombok.AllArgsConstructor; import lombok.Data; @@ -34,7 +34,7 @@ public class MergedRepository { private String relativeAccessPath; private boolean merged; private CommonRepository definedRepository; - private Map> mergedBindersMap; + private Map> mergedBindersMap; private CommonRepository executedRepository; private Integer order; diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java b/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java index b244b58c..1a5d82c3 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java @@ -17,10 +17,12 @@ package com.gitee.dorive.query.impl.executor; +import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; import com.gitee.dorive.core.entity.executor.Result; +import com.gitee.dorive.core.impl.binder.BoundBinder; import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.CommonRepository; @@ -82,7 +84,7 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { boolean abandoned = exampleWrapper.isAbandoned(); CommonRepository definedRepository = mergedRepository.getDefinedRepository(); - Map> mergedBindersMap = mergedRepository.getMergedBindersMap(); + Map> mergedBindersMap = mergedRepository.getMergedBindersMap(); CommonRepository executedRepository = mergedRepository.getExecutedRepository(); BinderResolver binderResolver = definedRepository.getBinderResolver(); @@ -109,9 +111,9 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { return; } - for (Map.Entry> entry : mergedBindersMap.entrySet()) { + for (Map.Entry> entry : mergedBindersMap.entrySet()) { String relativeAccessPath = entry.getKey(); - List binders = entry.getValue(); + List binders = entry.getValue(); ExampleWrapper targetExampleWrapper = exampleWrapperMap.get(relativeAccessPath); if (targetExampleWrapper != null) { if (entities.isEmpty()) { @@ -120,7 +122,7 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { } Example targetExample = targetExampleWrapper.getExample(); if (binders.size() == 1) { - StrongBinder binder = binders.get(0); + Binder binder = binders.get(0); List fieldValues = collectFieldValues(context, entities, binder); if (!fieldValues.isEmpty()) { String boundName = binder.getBoundName(); @@ -134,7 +136,16 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { } } else { - List aliases = binders.stream().map(StrongBinder::getBindAlias).collect(Collectors.toList()); + List aliases = binders.stream().map(binder -> { + if (binder instanceof BoundBinder) { + return ((BoundBinder) binder).getBindAlias(); + + } else if (binder instanceof StrongBinder) { + BoundBinder boundBinder = ((StrongBinder) binder).getBoundBinder(); + return boundBinder.getBindAlias(); + } + return null; + }).collect(Collectors.toList()); MultiInBuilder builder = new MultiInBuilder(aliases, entities.size()); collectFieldValues(context, entities, binders, builder); if (!builder.isEmpty()) { @@ -148,7 +159,7 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { }); } - private List collectFieldValues(Context context, List entities, StrongBinder binder) { + private List collectFieldValues(Context context, List entities, Binder binder) { List fieldValues = new ArrayList<>(entities.size()); for (Object entity : entities) { Object fieldValue = binder.getFieldValue(context, entity); @@ -160,9 +171,9 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { return fieldValues; } - private void collectFieldValues(Context context, List entities, List binders, MultiInBuilder builder) { + private void collectFieldValues(Context context, List entities, List binders, MultiInBuilder builder) { for (Object entity : entities) { - for (StrongBinder binder : binders) { + for (Binder binder : binders) { Object fieldValue = binder.getFieldValue(context, entity); if (fieldValue != null) { fieldValue = binder.output(context, fieldValue); diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java b/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java index 49ee4b35..a14dee02 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java @@ -18,6 +18,9 @@ package com.gitee.dorive.query.impl.resolver; import cn.hutool.core.util.StrUtil; +import com.gitee.dorive.core.api.binder.Binder; +import com.gitee.dorive.core.impl.binder.BoundBinder; +import com.gitee.dorive.core.impl.binder.ValueBinder; import com.gitee.dorive.query.entity.MergedRepository; import com.gitee.dorive.core.impl.binder.StrongBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; @@ -89,15 +92,22 @@ public class MergedRepositoryResolver { } } - private Map> getMergedBindersMap(String lastAccessPath, CommonRepository repository) { + private Map> getMergedBindersMap(String lastAccessPath, CommonRepository repository) { BinderResolver binderResolver = repository.getBinderResolver(); List strongBinders = binderResolver.getStrongBinders(); - Map> mergedBindersMap = new LinkedHashMap<>(); + List valueBinders = binderResolver.getValueBinders(); + Map> mergedBindersMap = new LinkedHashMap<>(); for (StrongBinder strongBinder : strongBinders) { - String relativeAccessPath = lastAccessPath + strongBinder.getBelongAccessPath(); - List existStrongBinders = mergedBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); + BoundBinder boundBinder = strongBinder.getBoundBinder(); + String relativeAccessPath = lastAccessPath + boundBinder.getBelongAccessPath(); + List existStrongBinders = mergedBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); existStrongBinders.add(strongBinder); } + for (ValueBinder valueBinder : valueBinders) { + String relativeAccessPath = lastAccessPath + valueBinder.getBelongAccessPath(); + List existStrongBinders = mergedBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); + existStrongBinders.add(valueBinder); + } return mergedBindersMap; } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java index e5054516..ed143d38 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java @@ -19,6 +19,7 @@ package com.gitee.dorive.sql.entity.segment; import lombok.AllArgsConstructor; import lombok.Data; +import org.apache.commons.lang3.StringUtils; @Data @AllArgsConstructor @@ -31,7 +32,11 @@ public class OnSegment { @Override public String toString() { - return tableAlias + "." + column + " = " + joinTableAlias + "." + joinColumn; + if (StringUtils.isNotBlank(tableAlias)) { + return tableAlias + "." + column + " = " + joinTableAlias + "." + joinColumn; + } else { + return column + " = " + joinTableAlias + "." + joinColumn; + } } } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index a493afe1..a91f14fb 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -25,7 +25,9 @@ import com.gitee.dorive.core.entity.common.EntityStoreInfo; import com.gitee.dorive.core.entity.executor.Criterion; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; +import com.gitee.dorive.core.impl.binder.BoundBinder; import com.gitee.dorive.core.impl.binder.StrongBinder; +import com.gitee.dorive.core.impl.binder.ValueBinder; import com.gitee.dorive.core.impl.executor.ExampleExecutor; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.AbstractContextRepository; @@ -92,7 +94,7 @@ public class SegmentBuilder { selectSegment.setTableSegment(tableSegment); } else { - List onSegments = newOnSegments(nodeMap, mergedRepository, node); + List onSegments = newOnSegments(context, nodeMap, mergedRepository, node); if (onSegments.isEmpty()) { nodeMap.remove(relativeAccessPath); continue; @@ -109,16 +111,18 @@ public class SegmentBuilder { return selectSegment; } - private List newOnSegments(Map nodeMap, MergedRepository mergedRepository, Node node) { + private List newOnSegments(Context context, Map nodeMap, MergedRepository mergedRepository, Node node) { String lastAccessPath = mergedRepository.getLastAccessPath(); CommonRepository definedRepository = mergedRepository.getDefinedRepository(); BinderResolver binderResolver = definedRepository.getBinderResolver(); List strongBinders = binderResolver.getStrongBinders(); + List valueBinders = binderResolver.getValueBinders(); TableSegment tableSegment = node.getTableSegment(); List onSegments = new ArrayList<>(strongBinders.size()); for (StrongBinder strongBinder : strongBinders) { - String relativeAccessPath = lastAccessPath + strongBinder.getBelongAccessPath(); + BoundBinder boundBinder = strongBinder.getBoundBinder(); + String relativeAccessPath = lastAccessPath + boundBinder.getBelongAccessPath(); Node targetNode = nodeMap.get(relativeAccessPath); if (targetNode != null) { TableSegment targetTableSegment = targetNode.getTableSegment(); @@ -126,8 +130,24 @@ public class SegmentBuilder { if (!children.contains(node)) { children.add(node); } - OnSegment onSegment = new OnSegment(tableSegment.getTableAlias(), strongBinder.getAlias(), - targetTableSegment.getTableAlias(), strongBinder.getBindAlias()); + OnSegment onSegment = new OnSegment( + tableSegment.getTableAlias(), strongBinder.getAlias(), + targetTableSegment.getTableAlias(), boundBinder.getBindAlias()); + onSegments.add(onSegment); + } + } + for (ValueBinder valueBinder : valueBinders) { + String relativeAccessPath = lastAccessPath + valueBinder.getBelongAccessPath(); + Node targetNode = nodeMap.get(relativeAccessPath); + if (targetNode != null) { + TableSegment targetTableSegment = targetNode.getTableSegment(); + List children = targetNode.getChildren(); + if (!children.contains(node)) { + children.add(node); + } + OnSegment onSegment = new OnSegment( + "", String.valueOf(valueBinder.getFieldValue(context, null)), + targetTableSegment.getTableAlias(), valueBinder.getBindAlias()); onSegments.add(onSegment); } } -- Gitee From 6e88dfa057e7e1089cfbc409fcd91c9c05e3ddc3 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 10:03:27 +0800 Subject: [PATCH 25/54] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/gitee/dorive/core/impl/binder/StrongBinder.java | 1 + .../gitee/dorive/core/impl/handler/BatchEntityHandler.java | 6 +++--- .../com/gitee/dorive/core/impl/resolver/BinderResolver.java | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java index 633c4d20..f9a6d592 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/StrongBinder.java @@ -32,6 +32,7 @@ public class StrongBinder extends FieldBinder { public StrongBinder(BindingDef bindingDef, Processor processor, PropChain fieldPropChain, String alias) { super(bindingDef, processor, fieldPropChain, alias); + this.boundBinder = new BoundBinder(bindingDef, processor); } @Override diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java index 58d2e3af..222bbb96 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java @@ -77,17 +77,17 @@ public class BatchEntityHandler implements EntityHandler { } List newEntities = new ArrayList<>(entities.size()); for (Object entity : entities) { - boolean flag = true; + boolean isValueEqual = true; for (ValueBinder valueBinder : valueBinders) { Object fieldValue = valueBinder.getFieldValue(context, null); Object boundValue = valueBinder.getBoundValue(context, entity); boundValue = valueBinder.input(context, boundValue); if (boundValue == null || !fieldValue.equals(boundValue.toString())) { - flag = false; + isValueEqual = false; break; } } - if (flag) { + if (isValueEqual) { newEntities.add(entity); } } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java index 930bb418..092c6ebb 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java @@ -57,6 +57,7 @@ public class BinderResolver { private PropChainResolver propChainResolver; private List allBinders; + private List valueBinders; private List strongBinders; // 决定了关联查询具体使用哪种实现 private Map> mergedBindersMap; @@ -64,7 +65,6 @@ public class BinderResolver { private List selfFields; private JoinType joinType; private List weakBinders; - private List valueBinders; public BinderResolver(AbstractContextRepository repository, EntityEle entityEle) { this.repository = repository; @@ -79,13 +79,13 @@ public class BinderResolver { List bindingDefs = entityEle.getBindingDefs(); this.allBinders = new ArrayList<>(bindingDefs.size()); + this.valueBinders = new ArrayList<>(bindingDefs.size()); this.strongBinders = new ArrayList<>(bindingDefs.size()); this.mergedBindersMap = new LinkedHashMap<>(bindingDefs.size() * 4 / 3 + 1); this.boundIdBinder = null; this.selfFields = new ArrayList<>(bindingDefs.size()); this.joinType = JoinType.UNION; this.weakBinders = new ArrayList<>(bindingDefs.size()); - this.valueBinders = new ArrayList<>(bindingDefs.size()); String fieldErrorMsg = "The field configured for @Binding does not exist within the entity! type: {}, field: {}"; for (BindingDef bindingDef : bindingDefs) { @@ -110,7 +110,7 @@ public class BinderResolver { if (bindingType == BindingType.STRONG) { StrongBinder strongBinder = new StrongBinder(bindingDef, processor, fieldPropChain, alias); - BoundBinder boundBinder = new BoundBinder(bindingDef, processor); + BoundBinder boundBinder = strongBinder.getBoundBinder(); initBoundBinder(bindingDef, boundBinder); allBinders.add(strongBinder); strongBinders.add(strongBinder); -- Gitee From 0b6a2033bf710856c7df734d8318678d88d01048 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 11:12:39 +0800 Subject: [PATCH 26/54] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8F=8D=E5=90=91?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dorive/query/entity/MergedRepository.java | 6 +- .../impl/executor/StepwiseQueryExecutor.java | 89 +++++++++++-------- .../resolver/MergedRepositoryResolver.java | 36 ++++---- 3 files changed, 77 insertions(+), 54 deletions(-) diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java b/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java index 113ccede..b957a4ef 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/entity/MergedRepository.java @@ -17,7 +17,8 @@ package com.gitee.dorive.query.entity; -import com.gitee.dorive.core.api.binder.Binder; +import com.gitee.dorive.core.impl.binder.StrongBinder; +import com.gitee.dorive.core.impl.binder.ValueBinder; import com.gitee.dorive.core.repository.CommonRepository; import lombok.AllArgsConstructor; import lombok.Data; @@ -34,7 +35,8 @@ public class MergedRepository { private String relativeAccessPath; private boolean merged; private CommonRepository definedRepository; - private Map> mergedBindersMap; + private Map> relativeValueBindersMap; + private Map> relativeStrongBindersMap; private CommonRepository executedRepository; private Integer order; diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java b/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java index 1a5d82c3..624e84c6 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/impl/executor/StepwiseQueryExecutor.java @@ -17,13 +17,12 @@ package com.gitee.dorive.query.impl.executor; -import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.entity.executor.Example; import com.gitee.dorive.core.entity.executor.InnerExample; import com.gitee.dorive.core.entity.executor.Result; -import com.gitee.dorive.core.impl.binder.BoundBinder; import com.gitee.dorive.core.impl.binder.StrongBinder; +import com.gitee.dorive.core.impl.binder.ValueBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.CommonRepository; import com.gitee.dorive.core.util.MultiInBuilder; @@ -40,6 +39,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; public class StepwiseQueryExecutor extends AbstractQueryExecutor { @@ -84,19 +84,17 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { boolean abandoned = exampleWrapper.isAbandoned(); CommonRepository definedRepository = mergedRepository.getDefinedRepository(); - Map> mergedBindersMap = mergedRepository.getMergedBindersMap(); + Map> relativeValueBindersMap = mergedRepository.getRelativeValueBindersMap(); + Map> relativeStrongBindersMap = mergedRepository.getRelativeStrongBindersMap(); CommonRepository executedRepository = mergedRepository.getExecutedRepository(); BinderResolver binderResolver = definedRepository.getBinderResolver(); - for (String relativeAccessPath : mergedBindersMap.keySet()) { - ExampleWrapper targetExampleWrapper = exampleWrapperMap.get(relativeAccessPath); - if (targetExampleWrapper != null) { - if (targetExampleWrapper.isAbandoned()) { - abandoned = true; - break; - } - } + if (!abandoned) { + abandoned = determineAbandon(exampleWrapperMap, relativeValueBindersMap.keySet()); + } + if (!abandoned) { + abandoned = determineAbandon(exampleWrapperMap, relativeStrongBindersMap.keySet()); } List entities; @@ -111,9 +109,21 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { return; } - for (Map.Entry> entry : mergedBindersMap.entrySet()) { - String relativeAccessPath = entry.getKey(); - List binders = entry.getValue(); + relativeValueBindersMap.forEach((relativeAccessPath, valueBinders) -> { + ExampleWrapper targetExampleWrapper = exampleWrapperMap.get(relativeAccessPath); + if (targetExampleWrapper != null) { + Example targetExample = targetExampleWrapper.getExample(); + for (ValueBinder valueBinder : valueBinders) { + Object fieldValue = valueBinder.getFieldValue(context, null); + if (fieldValue != null) { + String boundName = valueBinder.getBoundName(); + targetExample.eq(boundName, fieldValue); + } + } + } + }); + + relativeStrongBindersMap.forEach((relativeAccessPath, strongBinders) -> { ExampleWrapper targetExampleWrapper = exampleWrapperMap.get(relativeAccessPath); if (targetExampleWrapper != null) { if (entities.isEmpty()) { @@ -121,11 +131,11 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { return; } Example targetExample = targetExampleWrapper.getExample(); - if (binders.size() == 1) { - Binder binder = binders.get(0); - List fieldValues = collectFieldValues(context, entities, binder); + if (strongBinders.size() == 1) { + StrongBinder strongBinder = strongBinders.get(0); + List fieldValues = collectFieldValues(context, entities, strongBinder); if (!fieldValues.isEmpty()) { - String boundName = binder.getBoundName(); + String boundName = strongBinder.getBoundName(); if (fieldValues.size() == 1) { targetExample.eq(boundName, fieldValues.get(0)); } else { @@ -136,18 +146,11 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { } } else { - List aliases = binders.stream().map(binder -> { - if (binder instanceof BoundBinder) { - return ((BoundBinder) binder).getBindAlias(); - - } else if (binder instanceof StrongBinder) { - BoundBinder boundBinder = ((StrongBinder) binder).getBoundBinder(); - return boundBinder.getBindAlias(); - } - return null; - }).collect(Collectors.toList()); + List aliases = strongBinders.stream() + .map(binder -> binder.getBoundBinder().getBindAlias()) + .collect(Collectors.toList()); MultiInBuilder builder = new MultiInBuilder(aliases, entities.size()); - collectFieldValues(context, entities, binders, builder); + collectFieldValues(context, entities, strongBinders, builder); if (!builder.isEmpty()) { targetExample.getCriteria().add(builder.toCriterion()); } else { @@ -155,28 +158,40 @@ public class StepwiseQueryExecutor extends AbstractQueryExecutor { } } } - } + }); }); } - private List collectFieldValues(Context context, List entities, Binder binder) { + private boolean determineAbandon(Map exampleWrapperMap, Set relativeAccessPaths) { + for (String relativeAccessPath : relativeAccessPaths) { + ExampleWrapper targetExampleWrapper = exampleWrapperMap.get(relativeAccessPath); + if (targetExampleWrapper != null) { + if (targetExampleWrapper.isAbandoned()) { + return true; + } + } + } + return false; + } + + private List collectFieldValues(Context context, List entities, StrongBinder strongBinder) { List fieldValues = new ArrayList<>(entities.size()); for (Object entity : entities) { - Object fieldValue = binder.getFieldValue(context, entity); + Object fieldValue = strongBinder.getFieldValue(context, entity); if (fieldValue != null) { - fieldValue = binder.output(context, fieldValue); + fieldValue = strongBinder.output(context, fieldValue); fieldValues.add(fieldValue); } } return fieldValues; } - private void collectFieldValues(Context context, List entities, List binders, MultiInBuilder builder) { + private void collectFieldValues(Context context, List entities, List strongBinders, MultiInBuilder builder) { for (Object entity : entities) { - for (Binder binder : binders) { - Object fieldValue = binder.getFieldValue(context, entity); + for (StrongBinder strongBinder : strongBinders) { + Object fieldValue = strongBinder.getFieldValue(context, entity); if (fieldValue != null) { - fieldValue = binder.output(context, fieldValue); + fieldValue = strongBinder.output(context, fieldValue); builder.append(fieldValue); } else { builder.clearRemainder(); diff --git a/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java b/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java index a14dee02..7cd5aab9 100644 --- a/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java +++ b/dorive-query/src/main/java/com/gitee/dorive/query/impl/resolver/MergedRepositoryResolver.java @@ -18,15 +18,14 @@ package com.gitee.dorive.query.impl.resolver; import cn.hutool.core.util.StrUtil; -import com.gitee.dorive.core.api.binder.Binder; import com.gitee.dorive.core.impl.binder.BoundBinder; -import com.gitee.dorive.core.impl.binder.ValueBinder; -import com.gitee.dorive.query.entity.MergedRepository; import com.gitee.dorive.core.impl.binder.StrongBinder; +import com.gitee.dorive.core.impl.binder.ValueBinder; import com.gitee.dorive.core.impl.resolver.BinderResolver; import com.gitee.dorive.core.repository.AbstractContextRepository; import com.gitee.dorive.core.repository.AbstractRepository; import com.gitee.dorive.core.repository.CommonRepository; +import com.gitee.dorive.query.entity.MergedRepository; import lombok.Data; import org.apache.commons.lang3.StringUtils; @@ -79,7 +78,8 @@ public class MergedRepositoryResolver { relativeAccessPath, abstractContextRepository != null, repository, - getMergedBindersMap(lastAccessPath, repository), + getRelativeValueBindersMap(lastAccessPath, repository), + getRelativeStrongBindersMap(lastAccessPath, repository), executedRepository, mergedRepositoryMap.size() + 1); addMergedRepository(mergedRepository); @@ -92,23 +92,29 @@ public class MergedRepositoryResolver { } } - private Map> getMergedBindersMap(String lastAccessPath, CommonRepository repository) { + private Map> getRelativeValueBindersMap(String lastAccessPath, CommonRepository repository) { BinderResolver binderResolver = repository.getBinderResolver(); - List strongBinders = binderResolver.getStrongBinders(); List valueBinders = binderResolver.getValueBinders(); - Map> mergedBindersMap = new LinkedHashMap<>(); + Map> relativeValueBindersMap = new LinkedHashMap<>(); + for (ValueBinder valueBinder : valueBinders) { + String relativeAccessPath = lastAccessPath + valueBinder.getBelongAccessPath(); + List existBinders = relativeValueBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); + existBinders.add(valueBinder); + } + return relativeValueBindersMap; + } + + private Map> getRelativeStrongBindersMap(String lastAccessPath, CommonRepository repository) { + BinderResolver binderResolver = repository.getBinderResolver(); + List strongBinders = binderResolver.getStrongBinders(); + Map> relativeStrongBindersMap = new LinkedHashMap<>(); for (StrongBinder strongBinder : strongBinders) { BoundBinder boundBinder = strongBinder.getBoundBinder(); String relativeAccessPath = lastAccessPath + boundBinder.getBelongAccessPath(); - List existStrongBinders = mergedBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); - existStrongBinders.add(strongBinder); - } - for (ValueBinder valueBinder : valueBinders) { - String relativeAccessPath = lastAccessPath + valueBinder.getBelongAccessPath(); - List existStrongBinders = mergedBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); - existStrongBinders.add(valueBinder); + List existBinders = relativeStrongBindersMap.computeIfAbsent(relativeAccessPath, key -> new ArrayList<>(4)); + existBinders.add(strongBinder); } - return mergedBindersMap; + return relativeStrongBindersMap; } private void addMergedRepository(MergedRepository mergedRepository) { -- Gitee From 2cb66f4e8666a1fd1a7f039bb3795d82f294638a Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 11:24:58 +0800 Subject: [PATCH 27/54] =?UTF-8?q?=E4=BC=98=E5=8C=96sql=E6=8B=BC=E6=8E=A5?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dorive/sql/entity/segment/OnSegment.java | 21 +++++++++++++++---- .../sql/impl/segment/SegmentBuilder.java | 20 +++++++++--------- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java index ed143d38..c26cd839 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java @@ -27,16 +27,29 @@ public class OnSegment { private String tableAlias; private String column; + private String literal; private String joinTableAlias; private String joinColumn; + public OnSegment(String tableAlias, String column, String joinTableAlias, String joinColumn) { + this.tableAlias = tableAlias; + this.column = column; + this.joinTableAlias = joinTableAlias; + this.joinColumn = joinColumn; + } + + public OnSegment(String literal, String joinTableAlias, String joinColumn) { + this.literal = literal; + this.joinTableAlias = joinTableAlias; + this.joinColumn = joinColumn; + } + @Override public String toString() { - if (StringUtils.isNotBlank(tableAlias)) { - return tableAlias + "." + column + " = " + joinTableAlias + "." + joinColumn; - } else { - return column + " = " + joinTableAlias + "." + joinColumn; + if (StringUtils.isNotBlank(literal)) { + return literal + " = " + joinTableAlias + "." + joinColumn; } + return tableAlias + "." + column + " = " + joinTableAlias + "." + joinColumn; } } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index a91f14fb..e6ac50d7 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -115,14 +115,13 @@ public class SegmentBuilder { String lastAccessPath = mergedRepository.getLastAccessPath(); CommonRepository definedRepository = mergedRepository.getDefinedRepository(); BinderResolver binderResolver = definedRepository.getBinderResolver(); - List strongBinders = binderResolver.getStrongBinders(); List valueBinders = binderResolver.getValueBinders(); + List strongBinders = binderResolver.getStrongBinders(); TableSegment tableSegment = node.getTableSegment(); List onSegments = new ArrayList<>(strongBinders.size()); - for (StrongBinder strongBinder : strongBinders) { - BoundBinder boundBinder = strongBinder.getBoundBinder(); - String relativeAccessPath = lastAccessPath + boundBinder.getBelongAccessPath(); + for (ValueBinder valueBinder : valueBinders) { + String relativeAccessPath = lastAccessPath + valueBinder.getBelongAccessPath(); Node targetNode = nodeMap.get(relativeAccessPath); if (targetNode != null) { TableSegment targetTableSegment = targetNode.getTableSegment(); @@ -131,13 +130,14 @@ public class SegmentBuilder { children.add(node); } OnSegment onSegment = new OnSegment( - tableSegment.getTableAlias(), strongBinder.getAlias(), - targetTableSegment.getTableAlias(), boundBinder.getBindAlias()); + String.valueOf(valueBinder.getFieldValue(context, null)), + targetTableSegment.getTableAlias(), valueBinder.getBindAlias()); onSegments.add(onSegment); } } - for (ValueBinder valueBinder : valueBinders) { - String relativeAccessPath = lastAccessPath + valueBinder.getBelongAccessPath(); + for (StrongBinder strongBinder : strongBinders) { + BoundBinder boundBinder = strongBinder.getBoundBinder(); + String relativeAccessPath = lastAccessPath + boundBinder.getBelongAccessPath(); Node targetNode = nodeMap.get(relativeAccessPath); if (targetNode != null) { TableSegment targetTableSegment = targetNode.getTableSegment(); @@ -146,8 +146,8 @@ public class SegmentBuilder { children.add(node); } OnSegment onSegment = new OnSegment( - "", String.valueOf(valueBinder.getFieldValue(context, null)), - targetTableSegment.getTableAlias(), valueBinder.getBindAlias()); + tableSegment.getTableAlias(), strongBinder.getAlias(), + targetTableSegment.getTableAlias(), boundBinder.getBindAlias()); onSegments.add(onSegment); } } -- Gitee From 9f1001982d797796a89093ff0eaa06c27ee9a5ad Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 11:34:46 +0800 Subject: [PATCH 28/54] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=80=BC=E6=AF=94?= =?UTF-8?q?=E5=AF=B9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/core/impl/binder/BoundBinder.java | 8 ++++---- .../gitee/dorive/core/impl/binder/ValueBinder.java | 13 ++++++++++++- .../core/impl/handler/BatchEntityHandler.java | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java index f06655e8..45097562 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java @@ -32,10 +32,10 @@ public class BoundBinder implements Binder { protected BindingDef bindingDef; protected Processor processor; - private String belongAccessPath; - private CommonRepository belongRepository; - private PropChain boundPropChain; - private String bindAlias; + protected String belongAccessPath; + protected CommonRepository belongRepository; + protected PropChain boundPropChain; + protected String bindAlias; public BoundBinder(BindingDef bindingDef, Processor processor) { this.bindingDef = bindingDef; diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java index b9a9653d..3325517d 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/ValueBinder.java @@ -17,7 +17,9 @@ package com.gitee.dorive.core.impl.binder; +import cn.hutool.core.convert.Convert; import com.gitee.dorive.api.entity.def.BindingDef; +import com.gitee.dorive.api.entity.element.PropChain; import com.gitee.dorive.core.api.binder.Processor; import com.gitee.dorive.core.api.context.Context; import lombok.Getter; @@ -27,13 +29,22 @@ import lombok.Setter; @Setter public class ValueBinder extends BoundBinder { + private Object value; + public ValueBinder(BindingDef bindingDef, Processor processor) { super(bindingDef, processor); } + @Override + public void setBoundPropChain(PropChain boundPropChain) { + super.setBoundPropChain(boundPropChain); + Class genericType = boundPropChain.getEntityField().getGenericType(); + this.value = Convert.convert(genericType, bindingDef.getValue()); + } + @Override public Object getFieldValue(Context context, Object entity) { - return bindingDef.getValue(); + return value; } } diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java index 222bbb96..e603234f 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java @@ -82,7 +82,7 @@ public class BatchEntityHandler implements EntityHandler { Object fieldValue = valueBinder.getFieldValue(context, null); Object boundValue = valueBinder.getBoundValue(context, entity); boundValue = valueBinder.input(context, boundValue); - if (boundValue == null || !fieldValue.equals(boundValue.toString())) { + if (!fieldValue.equals(boundValue)) { isValueEqual = false; break; } -- Gitee From 1621ae655074a71b88477d6ff674d79da52adaa2 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 11:42:24 +0800 Subject: [PATCH 29/54] =?UTF-8?q?=E4=BC=98=E5=8C=96sql=E6=8B=BC=E6=8E=A5?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index e6ac50d7..30242baf 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -130,7 +130,7 @@ public class SegmentBuilder { children.add(node); } OnSegment onSegment = new OnSegment( - String.valueOf(valueBinder.getFieldValue(context, null)), + CriterionUtils.sqlParam(valueBinder.getFieldValue(context, null)), targetTableSegment.getTableAlias(), valueBinder.getBindAlias()); onSegments.add(onSegment); } -- Gitee From aa3c5f6ec014d11796dfc3305d1ba35258b0768f Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 15:09:43 +0800 Subject: [PATCH 30/54] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=85=B3=E8=81=94?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/gitee/dorive/core/impl/handler/BatchEntityHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java index e603234f..2a3fedc5 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java @@ -50,6 +50,9 @@ public class BatchEntityHandler implements EntityHandler { for (CommonRepository repository : this.repository.getSubRepositories()) { if (repository.matches(context)) { entities = filterByValueBinders(context, entities, repository); + if (entities.isEmpty()) { + continue; + } EntityJoiner entityJoiner = newEntityJoiner(repository, entities.size()); if (entityJoiner == null) { continue; -- Gitee From cfb07ae372157b2ea2fcebe058c9f417acc414f1 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 15:26:12 +0800 Subject: [PATCH 31/54] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dorive/core/impl/handler/BatchEntityHandler.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java index 2a3fedc5..4a690ef8 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/handler/BatchEntityHandler.java @@ -49,15 +49,15 @@ public class BatchEntityHandler implements EntityHandler { long totalCount = 0L; for (CommonRepository repository : this.repository.getSubRepositories()) { if (repository.matches(context)) { - entities = filterByValueBinders(context, entities, repository); - if (entities.isEmpty()) { + List newEntities = filterByValueBinders(context, entities, repository); + if (newEntities.isEmpty()) { continue; } - EntityJoiner entityJoiner = newEntityJoiner(repository, entities.size()); + EntityJoiner entityJoiner = newEntityJoiner(repository, newEntities.size()); if (entityJoiner == null) { continue; } - Example example = entityJoiner.newExample(context, entities); + Example example = entityJoiner.newExample(context, newEntities); if (example.isEmpty()) { continue; } @@ -65,7 +65,7 @@ public class BatchEntityHandler implements EntityHandler { Query query = operationFactory.buildQueryByExample(example); query.includeRoot(); Result result = repository.executeQuery(context, query); - entityJoiner.join(context, entities, result); + entityJoiner.join(context, newEntities, result); totalCount += result.getCount(); } } -- Gitee From f5fdb66c6bd0e1387c6b47e0acd166a65c889e9e Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 16:11:55 +0800 Subject: [PATCH 32/54] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=80=E4=B8=AA?= =?UTF-8?q?=E5=8F=8D=E5=90=91=E6=9F=A5=E8=AF=A2=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gitee/dorive/core/impl/binder/BoundBinder.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java index 45097562..23743447 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/binder/BoundBinder.java @@ -25,6 +25,7 @@ import com.gitee.dorive.core.api.context.Context; import com.gitee.dorive.core.repository.CommonRepository; import lombok.AllArgsConstructor; import lombok.Data; +import org.apache.commons.lang3.StringUtils; @Data @AllArgsConstructor @@ -59,6 +60,10 @@ public class BoundBinder implements Binder { @Override public String getBoundName() { + String bindField = bindingDef.getBindField(); + if (StringUtils.isNotBlank(bindField)) { + return bindField; + } return boundPropChain.getEntityField().getName(); } -- Gitee From 1f3e7793bb8b785e9bf57cd696a8c569b4dd13d5 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Tue, 19 Mar 2024 16:37:55 +0800 Subject: [PATCH 33/54] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/sql/entity/segment/OnSegment.java | 13 ++++++++----- .../dorive/sql/impl/segment/SegmentBuilder.java | 4 ++-- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java index c26cd839..7d0b6749 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/entity/segment/OnSegment.java @@ -27,9 +27,9 @@ public class OnSegment { private String tableAlias; private String column; - private String literal; private String joinTableAlias; private String joinColumn; + private String literal; public OnSegment(String tableAlias, String column, String joinTableAlias, String joinColumn) { this.tableAlias = tableAlias; @@ -38,16 +38,19 @@ public class OnSegment { this.joinColumn = joinColumn; } - public OnSegment(String literal, String joinTableAlias, String joinColumn) { - this.literal = literal; + public OnSegment(String joinTableAlias, String joinColumn, String literal) { this.joinTableAlias = joinTableAlias; this.joinColumn = joinColumn; + this.literal = literal; } @Override public String toString() { - if (StringUtils.isNotBlank(literal)) { - return literal + " = " + joinTableAlias + "." + joinColumn; + if (StringUtils.isNotBlank(tableAlias) && StringUtils.isNotBlank(column)) { + return tableAlias + "." + column + " = " + joinTableAlias + "." + joinColumn; + + } else if (StringUtils.isNotBlank(literal)) { + return joinTableAlias + "." + joinColumn + " = " + literal; } return tableAlias + "." + column + " = " + joinTableAlias + "." + joinColumn; } diff --git a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java index 30242baf..04d8ae63 100644 --- a/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java +++ b/dorive-sql/src/main/java/com/gitee/dorive/sql/impl/segment/SegmentBuilder.java @@ -130,8 +130,8 @@ public class SegmentBuilder { children.add(node); } OnSegment onSegment = new OnSegment( - CriterionUtils.sqlParam(valueBinder.getFieldValue(context, null)), - targetTableSegment.getTableAlias(), valueBinder.getBindAlias()); + targetTableSegment.getTableAlias(), valueBinder.getBindAlias(), + CriterionUtils.sqlParam(valueBinder.getFieldValue(context, null))); onSegments.add(onSegment); } } -- Gitee From 7019bf9b653de96e40e757120dd1fd6714a7ce81 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Wed, 20 Mar 2024 14:06:02 +0800 Subject: [PATCH 34/54] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ed00fcfb..4a75f0e9 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ com.gitee.digital-engine dorive-spring-boot-starter - 3.4.3.3 + 3.4.3.4 ``` @@ -84,7 +84,7 @@ public class Tenant { * property 绑定对象内部属性 */ @Entity - @Binding(field = "deptId", bindExp = "./departments", property = "id") + @Binding(field = "deptId", bindExp = "./departments", processExp = "#val.![id]", bindField = "id") private List users; } ``` -- Gitee From a6442575c2527c8cc8d5e8b56fed66bb596c7c3d Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Wed, 20 Mar 2024 18:23:16 +0800 Subject: [PATCH 35/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/api/annotation/Entity.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index 64073c48..b6da4af0 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -19,16 +19,70 @@ package com.gitee.dorive.api.annotation; import java.lang.annotation.*; +/** + * 实体注解
+ * 作用:声明一个类或字段的类型为实体
+ * 解释:实体是一种数据结构的具体表现形式,具有以下特征:
+ * 1、描述了实体和其他实体之间的关系。(一对一、一对多、多对多)
+ * 2、描述了实体字段和持久化数据的映射关系。
+ * 3、包含方法,可通过方法重写进行拓展。
+ * + * @author tao.chen + */ @Inherited @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD}) public @interface Entity { + /** + * 实体名称
+ * 作用:可作为选取器的筛选条件
+ */ String name() default ""; + /** + * 数据来源
+ * 作用:声明持久化数据操作的具体实现
+ * 注意:目前仅支持mybatis-plus框架中BaseMapper的子类
+ */ Class source() default Object.class; + /** + * 实体工厂
+ * 作用:声明实体构造的具体实现
+ * 解释:实体工厂通过映射关系,实现了实体与持久化数据之间的相互转换
+ * 多态的实现方式:
+ * 1、假设实体为User,子类分别为User1、User2
+ * 2、新建UserFactory继承于DefaultEntityFactory
+ * 3、重写UserFactory的reconstitute方法
+ *
+     * public Object reconstitute(Context context, Object persistent) {
+     *     User user = (User) super.reconstitute(context, persistent);
+     *     if (user.getType() == 1) {
+     *         return BeanUtil.copyProperties(user, User1.class);
+     *
+     *     } else if (user.getType() == 2) {
+     *         return BeanUtil.copyProperties(user, User2.class);
+     *     }
+     *     return user;
+     * }
+     * 
+ * 4、修改User的@Entity注解 + *
+     * @Entity(......, factory = UserFactory.class)
+     * public class User {
+     *     ......
+     * }
+     * 
+ * 5、在UserRepository中引入子类的仓储
+ *
+     * public class UserRepository extends MybatisPlusRepository<User, Integer> {
+     *     private final User1Repository user1Repository;
+     *     private final User2Repository user2Repository;
+     * }
+     * 
+ */ Class factory() default Object.class; Class repository() default Object.class; -- Gitee From 57da1dd9440b0f9bcd490714920d10a3726e5886 Mon Sep 17 00:00:00 2001 From: chentaoah <609580885@qq.com> Date: Wed, 20 Mar 2024 23:04:11 +0800 Subject: [PATCH 36/54] =?UTF-8?q?=E7=BE=8E=E5=8C=96=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/api/annotation/Entity.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index b6da4af0..afc85494 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -56,7 +56,8 @@ public @interface Entity { * 1、假设实体为User,子类分别为User1、User2
* 2、新建UserFactory继承于DefaultEntityFactory
* 3、重写UserFactory的reconstitute方法
- *
+     * 
{@code
+     * @Override
      * public Object reconstitute(Context context, Object persistent) {
      *     User user = (User) super.reconstitute(context, persistent);
      *     if (user.getType() == 1) {
@@ -67,21 +68,21 @@ public @interface Entity {
      *     }
      *     return user;
      * }
-     * 
- * 4、修改User的@Entity注解 - *
-     * @Entity(......, factory = UserFactory.class)
+     * }
+ * 4、修改User的@Entity注解
+ *
{@code
+     * @Entity(......, factory = UserFactory.class)
      * public class User {
      *     ......
      * }
-     * 
+ * }
* 5、在UserRepository中引入子类的仓储
- *
-     * public class UserRepository extends MybatisPlusRepository<User, Integer> {
+     * 
{@code
+     * public class UserRepository extends MybatisPlusRepository {
      *     private final User1Repository user1Repository;
      *     private final User2Repository user2Repository;
      * }
-     * 
+ * }
*/ Class factory() default Object.class; -- Gitee From 94872255492d30abb8115275689f922b4e97e0a0 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 09:42:48 +0800 Subject: [PATCH 37/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/gitee/dorive/api/annotation/Entity.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index afc85494..72fcffa6 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -54,8 +54,7 @@ public @interface Entity { * 解释:实体工厂通过映射关系,实现了实体与持久化数据之间的相互转换
* 多态的实现方式:
* 1、假设实体为User,子类分别为User1、User2
- * 2、新建UserFactory继承于DefaultEntityFactory
- * 3、重写UserFactory的reconstitute方法
+ * 2、新建UserFactory继承于DefaultEntityFactory,并重写reconstitute方法
*
{@code
      * @Override
      * public Object reconstitute(Context context, Object persistent) {
@@ -69,14 +68,14 @@ public @interface Entity {
      *     return user;
      * }
      * }
- * 4、修改User的@Entity注解
+ * 3、修改User的@Entity注解,指定实体工厂为UserFactory
*
{@code
      * @Entity(......, factory = UserFactory.class)
      * public class User {
      *     ......
      * }
      * }
- * 5、在UserRepository中引入子类的仓储
+ * 4、在UserRepository中引入子类的仓储
*
{@code
      * public class UserRepository extends MybatisPlusRepository {
      *     private final User1Repository user1Repository;
-- 
Gitee


From b25f787309239d5b98f05a59dea22174489371f1 Mon Sep 17 00:00:00 2001
From: "tao.chen1" 
Date: Thu, 21 Mar 2024 09:45:12 +0800
Subject: [PATCH 38/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../src/main/java/com/gitee/dorive/api/annotation/Entity.java   | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
index 72fcffa6..a2253f6a 100644
--- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
+++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
@@ -37,7 +37,7 @@ public @interface Entity {
 
     /**
      * 实体名称
- * 作用:可作为选取器的筛选条件
+ * 作用:可作为选取器的匹配条件
*/ String name() default ""; -- Gitee From ff802544b3938e74549fed7a2a34fdc68d582e23 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 10:01:50 +0800 Subject: [PATCH 39/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/api/annotation/Entity.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index a2253f6a..0011341d 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -21,7 +21,7 @@ import java.lang.annotation.*; /** * 实体注解
- * 作用:声明一个类或字段的类型为实体
+ * 说明:声明一个类或字段的类型为实体
* 解释:实体是一种数据结构的具体表现形式,具有以下特征:
* 1、描述了实体和其他实体之间的关系。(一对一、一对多、多对多)
* 2、描述了实体字段和持久化数据的映射关系。
@@ -37,20 +37,20 @@ public @interface Entity { /** * 实体名称
- * 作用:可作为选取器的匹配条件
+ * 说明:可作为选取器的匹配条件
*/ String name() default ""; /** * 数据来源
- * 作用:声明持久化数据操作的具体实现
+ * 说明:声明持久化数据操作的具体实现
* 注意:目前仅支持mybatis-plus框架中BaseMapper的子类
*/ Class source() default Object.class; /** * 实体工厂
- * 作用:声明实体构造的具体实现
+ * 说明:声明实体构造的具体实现
* 解释:实体工厂通过映射关系,实现了实体与持久化数据之间的相互转换
* 多态的实现方式:
* 1、假设实体为User,子类分别为User1、User2
@@ -85,12 +85,31 @@ public @interface Entity { */ Class factory() default Object.class; + /** + * 指定仓储
+ * 说明:指定一个特定的仓储,代替source操作数据
+ * 注意:不能和source与factory同时使用,优先级更高
+ */ Class repository() default Object.class; + /** + * 操作优先级
+ * 说明:可以改变插入、更新、删除的操作顺序
+ */ int priority() default 0; + /** + * 默认排序字段
+ * 说明:关联查询时,默认的排序字段
+ */ String sortBy() default ""; + /** + * 默认排序方式
+ * 说明:关联查询时,默认的排序方式。
+ * + * @see com.gitee.dorive.api.constant.Order + */ String order() default ""; } -- Gitee From e3f1d95e0858614065cf824740b71b7fdca684f7 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 10:03:35 +0800 Subject: [PATCH 40/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/gitee/dorive/api/annotation/Entity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index 0011341d..e5e2539d 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -106,7 +106,7 @@ public @interface Entity { /** * 默认排序方式
- * 说明:关联查询时,默认的排序方式。
+ * 说明:关联查询时,默认的排序方式
* * @see com.gitee.dorive.api.constant.Order */ -- Gitee From 129493c94bfea72c0ed4536bfb2f90510eee5114 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 10:35:38 +0800 Subject: [PATCH 41/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dorive/api/annotation/Aggregate.java | 41 ++++++++++++++++++- .../gitee/dorive/api/annotation/Entity.java | 20 +++++---- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java index f293f608..4c5e9c53 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java @@ -26,31 +26,70 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * 聚合注解
+ * 作用:声明字段的类型为聚合
+ * 解释:聚合一般指多个实体组装后的集合
+ * 使用说明:
+ * 1、在使用上,聚合注解基本等效于实体注解
+ * 2、在不指定仓储的情况下,框架会自动匹配实体与仓储
+ * 例如:
+ *
{@code
+ * @Aggregate
+ * private List users;
+ * 等效于:
+ * @Entity(repository = UserRepository.class)
+ * private List users;
+ * }
+ * + * @author tao.chen + */ @Entity @Inherited @Documented +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD}) public @interface Aggregate { + /** + * @see Entity + */ @AliasFor(annotation = Entity.class) String name() default ""; + /** + * @see Entity + */ @AliasFor(annotation = Entity.class) Class source() default Object.class; + /** + * @see Entity + */ @AliasFor(annotation = Entity.class) Class factory() default Object.class; + /** + * @see Entity + */ @AliasFor(annotation = Entity.class) Class repository() default Object.class; + /** + * @see Entity + */ @AliasFor(annotation = Entity.class) int priority() default 0; + /** + * @see Entity + */ @AliasFor(annotation = Entity.class) String sortBy() default ""; + /** + * @see Entity + */ @AliasFor(annotation = Entity.class) String order() default ""; diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index e5e2539d..9d667e1e 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -21,7 +21,7 @@ import java.lang.annotation.*; /** * 实体注解
- * 说明:声明一个类或字段的类型为实体
+ * 作用:声明一个类或字段的类型为实体
* 解释:实体是一种数据结构的具体表现形式,具有以下特征:
* 1、描述了实体和其他实体之间的关系。(一对一、一对多、多对多)
* 2、描述了实体字段和持久化数据的映射关系。
@@ -37,20 +37,20 @@ public @interface Entity { /** * 实体名称
- * 说明:可作为选取器的匹配条件
+ * 作用:可作为选取器的匹配条件
*/ String name() default ""; /** * 数据来源
- * 说明:声明持久化数据操作的具体实现
+ * 作用:声明持久化数据操作的具体实现
* 注意:目前仅支持mybatis-plus框架中BaseMapper的子类
*/ Class source() default Object.class; /** * 实体工厂
- * 说明:声明实体构造的具体实现
+ * 作用:声明实体构造的具体实现
* 解释:实体工厂通过映射关系,实现了实体与持久化数据之间的相互转换
* 多态的实现方式:
* 1、假设实体为User,子类分别为User1、User2
@@ -87,26 +87,28 @@ public @interface Entity { /** * 指定仓储
- * 说明:指定一个特定的仓储,代替source操作数据
- * 注意:不能和source与factory同时使用,优先级更高
+ * 作用:指定一个特定的仓储,代替source操作数据
+ * 注意:
+ * 1、不能和source与factory同时使用,优先级更高
+ * 2、一般在需要查询实体内部其他实体时,才指定仓储
*/ Class repository() default Object.class; /** * 操作优先级
- * 说明:可以改变插入、更新、删除的操作顺序
+ * 作用:可以改变插入、更新、删除的操作顺序
*/ int priority() default 0; /** * 默认排序字段
- * 说明:关联查询时,默认的排序字段
+ * 作用:关联查询时,默认的排序字段
*/ String sortBy() default ""; /** * 默认排序方式
- * 说明:关联查询时,默认的排序方式
+ * 作用:关联查询时,默认的排序方式
* * @see com.gitee.dorive.api.constant.Order */ -- Gitee From b1238753dfa05895555a0761543ee649b351d797 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 10:36:26 +0800 Subject: [PATCH 42/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/gitee/dorive/api/annotation/Aggregate.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java index 4c5e9c53..42c53c0d 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java @@ -32,7 +32,7 @@ import java.lang.annotation.Target; * 解释:聚合一般指多个实体组装后的集合
* 使用说明:
* 1、在使用上,聚合注解基本等效于实体注解
- * 2、在不指定仓储的情况下,框架会自动匹配实体与仓储
+ * 2、在不指定仓储的情况下,框架会自动为实体匹配仓储
* 例如:
*
{@code
  * @Aggregate
-- 
Gitee


From 895b8f4f8c9ea508a5d3b3be17bf30a7cc44ae74 Mon Sep 17 00:00:00 2001
From: "tao.chen1" 
Date: Thu, 21 Mar 2024 10:52:52 +0800
Subject: [PATCH 43/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../gitee/dorive/api/annotation/Entity.java   | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
index 9d667e1e..e5509401 100644
--- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
+++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
@@ -22,10 +22,36 @@ import java.lang.annotation.*;
 /**
  * 实体注解
* 作用:声明一个类或字段的类型为实体
+ *

* 解释:实体是一种数据结构的具体表现形式,具有以下特征:
* 1、描述了实体和其他实体之间的关系。(一对一、一对多、多对多)
* 2、描述了实体字段和持久化数据的映射关系。
* 3、包含方法,可通过方法重写进行拓展。
+ *

+ *

+ * 额外说明:
+ * 1、在类上声明的实体注解,代表该实体的默认配置
+ *

{@code
+ * @Entity(name = "user", source = UserMapper.class)
+ * public class User {
+ *     ......
+ * }
+ * }
+ * 2、在字段上声明的实体注解,代表在当前实体内的配置
+ *
{@code
+ * public class Dept {
+ *     @Entity(name = "user1", source = UserMapper1.class)
+ *     private List users;
+ * }
+ * }
+ * 3、一般情况下,使用默认配置即可
+ *
{@code
+ * public class Dept {
+ *     @Entity
+ *     private List users;
+ * }
+ * }
+ *

* * @author tao.chen */ -- Gitee From fb347a75ec0dbb3a6473996b49fe08507be9f178 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 11:05:33 +0800 Subject: [PATCH 44/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../gitee/dorive/api/annotation/Entity.java | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index e5509401..88e3d91b 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -29,7 +29,7 @@ import java.lang.annotation.*; * 3、包含方法,可通过方法重写进行拓展。
*

*

- * 额外说明:
+ * 使用说明:
* 1、在类上声明的实体注解,代表该实体的默认配置
*

{@code
  * @Entity(name = "user", source = UserMapper.class)
@@ -52,6 +52,22 @@ import java.lang.annotation.*;
  * }
  * }
*

+ *

+ * 补充说明:如果想要在关联查询时,查询实体内部的其他实体,请指定仓储
+ *

{@code
+ * @Entity(name = "dept", source = DeptMapper.class)
+ * public class Dept {
+ *     @Entity(repository = UserRepository.class)
+ *     private List users;
+ * }
+ *
+ * @Entity(name = "user", source = UserMapper.class)
+ * public class User {
+ *     @Entity
+ *     private List roles;
+ * }
+ * }
+ *

* * @author tao.chen */ -- Gitee From a698453cc21cbd7dcbf8e6ca6233ea032d303567 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 11:08:04 +0800 Subject: [PATCH 45/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/gitee/dorive/api/annotation/Entity.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index 88e3d91b..5ec1b046 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -130,9 +130,7 @@ public @interface Entity { /** * 指定仓储
* 作用:指定一个特定的仓储,代替source操作数据
- * 注意:
- * 1、不能和source与factory同时使用,优先级更高
- * 2、一般在需要查询实体内部其他实体时,才指定仓储
+ * 注意:不能和source与factory同时使用,优先级更高
*/ Class repository() default Object.class; -- Gitee From 2bcd549cbb195c41ad2fe970212e98cd06fc322b Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 11:10:46 +0800 Subject: [PATCH 46/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/gitee/dorive/api/annotation/Entity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index 5ec1b046..d38b769f 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -67,9 +67,10 @@ import java.lang.annotation.*; * private List roles; * } * }
- *

* * @author tao.chen + * @see Aggregate + *

*/ @Inherited @Documented -- Gitee From 56a81913bdbadfad2983d8d49551f3c51034b15e Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 11:14:17 +0800 Subject: [PATCH 47/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/gitee/dorive/api/annotation/Entity.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index d38b769f..5ec1b046 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -67,10 +67,9 @@ import java.lang.annotation.*; * private List roles; * } * }
+ *

* * @author tao.chen - * @see Aggregate - *

*/ @Inherited @Documented -- Gitee From 07998a176b501d15991debdd7bb74f3318e14cd4 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 11:17:46 +0800 Subject: [PATCH 48/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gitee/dorive/api/annotation/Entity.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java index 5ec1b046..3ca7d1ec 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java @@ -55,17 +55,17 @@ import java.lang.annotation.*; *

* 补充说明:如果想要在关联查询时,查询实体内部的其他实体,请指定仓储
*

{@code
- * @Entity(name = "dept", source = DeptMapper.class)
- * public class Dept {
- *     @Entity(repository = UserRepository.class)
- *     private List users;
- * }
- *
  * @Entity(name = "user", source = UserMapper.class)
  * public class User {
  *     @Entity
  *     private List roles;
  * }
+ *
+ * @Entity(name = "dept", source = DeptMapper.class)
+ * public class Dept {
+ *     @Entity(repository = UserRepository.class)
+ *     private List users;
+ * }
  * }
*

* -- Gitee From ace09eed6028587fcb3e9e43f7f8fa572d0613cf Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 11:26:06 +0800 Subject: [PATCH 49/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/gitee/dorive/api/annotation/Aggregate.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java index 42c53c0d..d9829a94 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java @@ -30,17 +30,23 @@ import java.lang.annotation.Target; * 聚合注解
* 作用:声明字段的类型为聚合
* 解释:聚合一般指多个实体组装后的集合
+ *

* 使用说明:
* 1、在使用上,聚合注解基本等效于实体注解
* 2、在不指定仓储的情况下,框架会自动为实体匹配仓储
+ *

+ *

* 例如:
*

{@code
  * @Aggregate
  * private List users;
+ *
  * 等效于:
+ *
  * @Entity(repository = UserRepository.class)
  * private List users;
  * }
+ *

* * @author tao.chen */ -- Gitee From b26109e334e74ccc80fae71da402ad8fd41f896b Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 11:42:49 +0800 Subject: [PATCH 50/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/gitee/dorive/api/annotation/Aggregate.java | 5 +++++ .../main/java/com/gitee/dorive/api/annotation/Entity.java | 3 +++ 2 files changed, 8 insertions(+) diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java index d9829a94..3e513f3f 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Aggregate.java @@ -29,12 +29,17 @@ import java.lang.annotation.Target; /** * 聚合注解
* 作用:声明字段的类型为聚合
+ * + *

* 解释:聚合一般指多个实体组装后的集合
+ *

+ * *

* 使用说明:
* 1、在使用上,聚合注解基本等效于实体注解
* 2、在不指定仓储的情况下,框架会自动为实体匹配仓储
*

+ * *

* 例如:
*

{@code
diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
index 3ca7d1ec..411f5ea0 100644
--- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
+++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Entity.java
@@ -22,12 +22,14 @@ import java.lang.annotation.*;
 /**
  * 实体注解
* 作用:声明一个类或字段的类型为实体
+ * *

* 解释:实体是一种数据结构的具体表现形式,具有以下特征:
* 1、描述了实体和其他实体之间的关系。(一对一、一对多、多对多)
* 2、描述了实体字段和持久化数据的映射关系。
* 3、包含方法,可通过方法重写进行拓展。
*

+ * *

* 使用说明:
* 1、在类上声明的实体注解,代表该实体的默认配置
@@ -52,6 +54,7 @@ import java.lang.annotation.*; * } * }

*

+ * *

* 补充说明:如果想要在关联查询时,查询实体内部的其他实体,请指定仓储
*

{@code
-- 
Gitee


From 35692dc291004c7b704777baa24c7614b48b47eb Mon Sep 17 00:00:00 2001
From: "tao.chen1" 
Date: Thu, 21 Mar 2024 16:01:03 +0800
Subject: [PATCH 51/54] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../gitee/dorive/api/annotation/Binding.java  | 66 ++++++++++++++++++-
 .../gitee/dorive/api/annotation/Bindings.java |  4 +-
 .../dorive/api/entity/def/BindingDef.java     |  2 +-
 .../core/impl/resolver/BinderResolver.java    |  6 +-
 4 files changed, 69 insertions(+), 9 deletions(-)

diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java
index a075c775..ecf6f07b 100644
--- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java
+++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Binding.java
@@ -19,23 +19,83 @@ package com.gitee.dorive.api.annotation;
 
 import java.lang.annotation.*;
 
+/**
+ * 绑定注解
+ * 作用:声明实体字段之间的绑定关系
+ * + *

+ * 绑定类型有三种,分别是:
+ * 1、强绑定(两个字段强相关时使用)
+ *

{@code
+ * @Binding(field = "field", bindExp = "./field")
+ * }
+ * 2、弱绑定(字段与上下文相关时使用)
+ *
{@code
+ * @Binding(field = "field", processExp = "#ctx['field']")
+ * }
+ * 3、值绑定(字段与字面值相关时使用)
+ *
{@code
+ * @Binding(value = "0", bindExp = "./field")
+ * }
+ *

+ * + *

+ * 当绑定的字段,还需要再进行深度解析时,请使用以下配置方式:
+ *

{@code
+ * @Binding(field = "field", bindExp = "./list", processExp = "#val.![field]", bindField = "field")
+ * }
+ *

+ * + * @author tao.chen + */ @Inherited @Documented +@Target(ElementType.FIELD) @Repeatable(Bindings.class) @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD}) public @interface Binding { + /** + * 字段名称
+ * 作用:声明当前实体绑定的字段
+ */ String field() default ""; + /** + * 字面值
+ * 作用:值绑定时使用
+ */ String value() default ""; + /** + * 绑定表达式
+ * 作用:声明上下文绑定的字段
+ * 说明:
+ * 1、./field是以当前实体为参考系的相对路径
+ * 2、/field是以聚合根为参考系的绝对路径
+ */ String bindExp() default ""; + /** + * 加工表达式
+ * 作用:从上下文中获取信息,或加工绑定的字段
+ * 说明:表达式书写请参考SpEL
+ * + * @see org.springframework.expression + */ String processExp() default ""; - String bindField() default ""; - + /** + * 指定加工器
+ * 作用:声明字段加工的具体实现
+ * 说明:如果加工表达式不为空,默认实现为SpELProcessor
+ */ Class processor() default Object.class; + /** + * 绑定的字段名称
+ * 作用:显式声明绑定的字段名称
+ */ + String bindField() default ""; + } diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Bindings.java b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Bindings.java index 8e7d9394..77f28544 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Bindings.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/annotation/Bindings.java @@ -21,10 +21,8 @@ import java.lang.annotation.*; @Inherited @Documented +@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.TYPE, ElementType.FIELD}) public @interface Bindings { - Binding[] value(); - } diff --git a/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java b/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java index c4678188..6538b8f8 100644 --- a/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java +++ b/dorive-api/src/main/java/com/gitee/dorive/api/entity/def/BindingDef.java @@ -40,8 +40,8 @@ public class BindingDef { private String value; private String bindExp; private String processExp; - private String bindField; private Class processor; + private String bindField; public static List fromElement(AnnotatedElement element) { Set bindingAnnotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(element, Binding.class); diff --git a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java index 092c6ebb..30c1b6a3 100644 --- a/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java +++ b/dorive-core/src/main/java/com/gitee/dorive/core/impl/resolver/BinderResolver.java @@ -165,10 +165,11 @@ public class BinderResolver { private BindingDef renewBindingDef(String accessPath, BindingDef bindingDef) { bindingDef = BeanUtil.copyProperties(bindingDef, BindingDef.class); String field = StrUtil.trim(bindingDef.getField()); + String value = StrUtil.trim(bindingDef.getValue()); String bindExp = StrUtil.trim(bindingDef.getBindExp()); String processExp = StrUtil.trim(bindingDef.getProcessExp()); - String bindField = StrUtil.trim(bindingDef.getBindField()); Class processor = bindingDef.getProcessor(); + String bindField = StrUtil.trim(bindingDef.getBindField()); if (bindExp.startsWith(".")) { bindExp = PathUtils.getAbsolutePath(accessPath, bindExp); @@ -184,10 +185,11 @@ public class BinderResolver { } bindingDef.setField(field); + bindingDef.setValue(value); bindingDef.setBindExp(bindExp); bindingDef.setProcessExp(processExp); - bindingDef.setBindField(bindField); bindingDef.setProcessor(processor); + bindingDef.setBindField(bindField); return bindingDef; } -- Gitee From 77c7587a071e6237ef4b966b00d70f7584ad5009 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 18:17:16 +0800 Subject: [PATCH 52/54] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 159 +++++++----------------------------------- doc/img/framework.png | Bin 25864 -> 97137 bytes 2 files changed, 25 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index 4a75f0e9..52a6ea3d 100644 --- a/README.md +++ b/README.md @@ -7,41 +7,42 @@ stars


-🔥🔥🔥轻量的领域驱动的ORM框架,帮助开发者通过建模,快速构建具有可维护性、可拓展性的应用程序。 +### 📚简介 -### 👍推荐理由 +​ dorive是一个轻量的领域驱动式业务框架,它提供了诸多开箱即用的功能,旨在帮助开发者快速、便捷地在项目中应用领域驱动,并从中受益。 -- **模型驱动** -- **NoSQL** -- **面向对象** -- **事件模式** -- **代码生成** +​ 这些功能涵盖了依赖注入校验、依赖即用配置、实体定义与映射、级联查询与操作、实体多态、实体事件通知、复杂推导查询、ref关键字、复杂计数统计、表结构生成、数据库逆向生成、代码生成等,覆盖了大部分开发场景。 -### 🤼‍♂️设计模式对比 +### 🎁名称由来 -| | UI驱动 | 领域驱动 | -| -------- | --------- | -------------- | -| 编码风格 | 面向过程 | ✅面向对象 | -| 上手难度 | ✅简单 | 中等 | -| 开发速度 | ✅快 | 中等 | -| 设计依据 | 原型 | ✅核心业务 | -| 交付周期 | ✅小、精确 | 一般以整体交付 | -| 技术依赖 | 数据库 | ✅无 | -| 耦合度 | 极高 | ✅低 | -| 可维护性 | 差 | ✅高 | -| 可复用性 | 差 | ✅高 | -| 可拓展性 | 差 | ✅高 | +​ dorive = domain + driven 或 do + driven ,是原公司项目沉淀后的开源库。“do”表明了一种态度,只有付诸行动,才能有所收获。 -### 🤸面临的挑战 +### 🍺设计理念 -- 业务知识的提炼与抽象 -- 面向对象编程 + 数据持久化(框架发力点) +​ dorive提供统一的api,帮助开发者从繁复的增删改查中解脱出来,从而把精力集中到业务逻辑开发中。理想状态下,开发者无需再编写sql语句,也将无惧于快速迭代导致的频繁改动。 ### 📊架构设计 ![avatar](https://gitee.com/digital-engine/dorive/raw/master/doc/img/framework.png) -### 🏄快速开始 +### 🛠️模块说明 + +| 模块 | 说明 | +| -------------------------- | ---------------------------------------------------- | +| dorive-inject | 实现了依赖注入校验 | +| dorive-env | 实现了依赖即用配置 | +| dorive-web | 提供了web开发时会用到的工具类 | +| dorive-proxy | 动态代理工具包 | +| dorive-api | 包含领域驱动实体定义规范 | +| dorive-core | 核心实现(实体定义与映射、级联查询与操作、实体多态) | +| dorive-event | 实现了实体事件通知 | +| dorive-query | 实现了复杂推导查询 | +| dorive-ref | 实现了ref关键字 | +| dorive-sql | 实现了复杂计数统计 | +| dorive-mybatis-plus | 提供基于mybatis-plus的实现 | +| dorive-spring-boot-starter | 依赖管理启动器 | + +### 📦安装 ```xml @@ -51,113 +52,3 @@ ``` -### 🧡代码示例 - -以Saas化场景为例,为租户建模。 - -#### 定义实体 - -```java -/** - * 租户聚合 - * name 实体名称 - * source 数据来源 - */ -@Data -@Entity(name = "tenant", source = SysTenantMapper.class) -public class Tenant { - - private Integer id; - private String tenantCode; - - /** - * 部门实体 - * field 字段名称 - * bindExp 绑定字段表达式 - */ - @Entity - @Binding(field = "tenantId", bindExp = "./id") - private List departments; - - /** - * 用户实体 - * property 绑定对象内部属性 - */ - @Entity - @Binding(field = "deptId", bindExp = "./departments", processExp = "#val.![id]", bindField = "id") - private List users; -} -``` - -#### 定义仓储 - -```java -@RootRepository -@QueryScan("xxx.xxx.xxx.xxx.xxx.query") -public class TenantRepository extends MybatisPlusRepository { -} -``` - -#### 定义查询对象 - -```java -package xxx.xxx.xxx.xxx.xxx.query; -@Data -@Example -public class TenantQuery { - @Criterion(belongTo = "user") - private String userCode; - private String sortBy; - private String order; - private Integer page; - private Integer limit; -} -``` - -#### 新增数据 - -```java -// 开发者无需设置实体之间的关联id -Tenant tenant = new Tenant(); -tenant.setTenantCode("tenant"); - -Department department = new Department(); -department.setDeptCode("dept"); -tenant.setDepartments(Collections.singletonList(department)); - -User user = new User(); -user.setUserCode("user"); -tenant.setUser(Collections.singletonList(user)); - -int count = tenantRepository.insert(Selector.ALL, tenant); -``` - -#### 查询数据 - -```java -// 开发者无需编写复杂的查询SQL -TenantQuery tenantQuery = new TenantQuery(); -tenantQuery.setUserCode("000001"); -tenantQuery.setSortBy("id"); -tenantQuery.setOrder("desc"); -tenantQuery.setPage(1); -tenantQuery.setLimit(10); - -List tenants = tenantRepository.selectByQuery(Selector.ALL, tenantQuery); -``` - -#### 更新数据 - -```java -Tenant tenant = tenantRepository.selectByPrimaryKey(Selector.ROOT, 1); -tenant.setTenantCode("tenant1"); - -int count = tenantRepository.update(Selector.ROOT, tenant); -``` - -#### 删除数据 - -```java -// 开发者通过聚合对象的id,即可删除所有数据 -int count = tenantRepository.deleteByPrimaryKey(Selector.ALL, 1); -``` diff --git a/doc/img/framework.png b/doc/img/framework.png index b8a0004ef5d4b4f78643cdae3bfecc3a7f8f0de8..66c6ed825c8788c12076c98b3e7b929d31cc8010 100644 GIT binary patch literal 97137 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 UG<*5{7Wkv7s;g40bRz8k0i@VzBme*a literal 25864 zcmeFZWmuHm7e0zZ3rHzQqX>wkAl)h@CEWu^NsLIBFbp6lDJ_kpN_Pz~fPi!}G)Omt zbj_T{*Z0-mIiJpTosZ}LiHps%_p_e0*IsMwd)c6TH*t1*f-4s=*ZPO0oA48W0#L zg4S?asb~~&?!f$)Lc96keE0kBlO(|-OGfg4YA8&`^tU+M5l)Gn3$wu~s_$v{k7zr$T@w*ulQp3tK;l|W@Ifh z)-?hdED)JL7LKMILTT*fZDO21WBz2GIM@FCsz`GU2i#5H{rb<}u|QF`frS6bK}1OGMNQ(>|ZPY^8m9I4w)y1%l$UkpBV%aFMhxJ*8@4>SRh`j8&)EJW?;b- zi2s$184DIiF0+u^`-vF;A7OvZU`DjXPBiYvpBdM17$5%6*U4kS68S^; z>Hqs&W`6_=-oK)g`O9F!A|0T)!=2S@Fl z8IfV7&Twwz(i_Jb5u)pGDJ4@6`n51ACn`2BPC_`}oioQ6^teqgIIED*eUEj~7%8_P zcQfn}(LbiJqksjYh7Fh?kHWGuWds(typ>ER(k2heEqw^9WazL7TQl{4K3(8Z3Us%* zct8kmbU3s+%TwfseB?@>$7{Y_fZR2c!~x@!DaQU|*@Nl8IZ!?JVWzB#di~Rb`uoqf zs?*#jxp+dCFIk%yTbXf(9v#^6G>=h%}lJ2h%!H zv)J$D^IoRVCqBaeEv6h63~33)=XbPv`^?N2T<7MBF4VQ96=X*F@oZGiKDn=jq^DmpYh8c#q~UEYyK&copeU8?jekzt zR3a9rq^s$~YUS8~j{5uD4$E9XUeJ3PC1{gR>{|}Mh8!1nn+b_e%c`Vw`+}y!><=ta z;r>RoK1C)R)3xGs>x8v?&ywqW4no*by}{&;hF%#}J`Doz(1QM8$>v$p#7E{H&8%uM z=mq;kVsPwI%<4Y}GqVvml*!wBA92&k<=J=UxVH$uKxetXqZNi(niy(U_&rSz2sv5S z$9Ib@@-!kAE*x%x96hRsreXFDn1r0IOM^zXhTz|8VSyk|erZULw6eH=i>Fk#WbTU^ z3#!*DQuSe?@R6;M?kPssJ=FcAvV^@-MSd@;0XJKib(`AUI&?gvrC@q@7BtYzGVyNc z6;!CatN4@XZ$Bb44R|@h$uZc{lgGFp)R)f&KG40ac)YO(6ZDk-cGclAJ}0k~-FW1B z&FHa!X9I4!aFKnUa${P}Tyj$*PP%9V?`vyQ6L+7^`Z!UwV2&xaPS!rzUrtfR2k>`t z3w!=p*BN79jL#+64yd1vk0cQr6?I13?vW&?^8V~Z0xH@5)X`KHC;m0}RYM@D`b3%7sgM^M;}JZl(obK}403D+RYa8{UpfDU@%cPki}3 z8!(OL4Zcae3qSe@-~@{?YhU^|8ujJ3*Lw+OpN#x=<*-o-z;%yT5flIGx?%3D{s?=$ z0fATlIb%&_z&jfv`T71En2HB1&O~yc_8%btg(z~vz7fL1GE(_m*ZLzV1=LnwBHc)% zv-h0^HH+~$oyPNocy8bRFZ9TQic=Do9OAP{9@H1a4ZUDrk`T064ZWol7*Ei3*rLEj zvhiS*=k*FYei<%q`=Ru2P|;>C)i2(4Gg-s*i@Tj=-TPoyhY*LhEc1#uV>E_Q_pn;< z{*#gUx$reTX?K&gU|pid1VVTey80r=c)YGJKC+1J?ydhM_m{a#D5BS&R&|+D#9zb; zay$;$tuPUlwjImdp#E1Q*9e}fBhLC~403wU|qM9Bv*tybwL>r_46}z;|XeA7URqW~G&Yh8} zqa&AUGs0c`4{`s5I75|%D%rq`8;u92Rntr5EJ85hi|BNg*(`9Vz1iX4P#E(V;dYek zm0wuP!{hUU(sNB(;>;*=3CxH@tp8Qry>|$vwSL2gcm8>Xnrq!lN7Dg2jEMQ_PfUi{ z5r`BYB)X^>_?Ze?(Q#9TAyOJMZGt(u9Y*yxpZv)Nj0kIsU8dn$38u6mS7iinBF5dO z$B`rDZRkZTB_`l>*b~qTF5WYFb6WH0tIL}%|Eg(B=799UoNxG9_o3;_*1@*pvy-&9 z;69^cT*-2D^D}6}>u#j$X01!S$HfsMS^O}g;MJ=>#QH73v>2`_c)#i|yfw0TTb~QG zcNx;KCK~AirpL}A1r9(XL8>omM%B;j!WmXaNK0NUuFl(9u1yR-a+r7o`G`0`o&?SJK7@ANgq%MKA%C3dh7;QEF(BzS zhItB+F$OuNAFL96OxS=F>)4ST+%YhlN3Iu%KOGOs!U~wtqm5-}r)?4=kiwj=%4gVolc)D8GB8xgc4O3+i);}*$dERYmPO@k@LWiwP% z=}({NisCK z+Sr8x82}unnG|P4l0=hXG#M5%m^fq-k&u9dsdXZ1`RHr(c`={twD`X+(BtF(Grt^ zbb$p7ez1iH{YeYQC}kFO20~;0q^yYRAOZ=_nV{%DBbna7vYQ4PzdiD=u%<7I1+#gZ z!uNMXTOA9gC&~E?(8#~@I02_y_?lAw&j@UjkQw#WMo#I^2{*&NhLe8lRjcBkk>`>? zf__wZpYYH5E(DU?idNOze@DKu`y&d{brzMWE^?asB7Zi)@eArPz12IS)shwo)&L0LYP+k(&a?i;$gII4nB>AhmAkht4yt{c7KGcbVT+rf>ztTFU3QrFsH|%2K=7K-(GeN zbMsnN{#2kjD(=;{7F|6j{Mb9!Vnh9CcVK!ixB^J;it2=wW%Sj|M$3xprQ?tvutze6 ze2!p+x>xsD>fq0(>8s_}-JqW{i+2w@3)k5BJrka8Pi4?JbuNOGXLRC|ENpJMuD^Zz zwR;Nuy|Cz_EsbI7=Uq~zY2S^>jE%UwOA~OmRx%C8v8bW5LUm=hKc^(ljYhtbvWlst z6`ZcaNY|U!xl#UJB8=#+H|Kb(bQ&1ljocLR(FO{qM0o`!cPCl=UPWYB=w#0E zVvShk5$QJvT8rZk2M3UcfdL3*&%5QX24U zdSd<6pK}&Rg^D*q%fx=S=j9P}KOkmzJ-)s^Ol)<^6`pgskFly;#Q)|nWih^8opv1W zmt>(k?0pACu|27`x?G9=bW_1JV6qc3_2GSSp2<9uM@{rGvPQ}K{Ht2shM!Vo+$YtD zlQ*@Q*z@)$+C`INSBLRw^u8~&76i?9JSU3U4M>A;o^PAXGwuPB!^}o z(T-IotnFOyC--R|rqm)r2Qwe-eFbhHw?!fzHLi=fGYGW~jMsNwxnzhbOxRsvrXZ0| zy!KNpGj~obAuP9DoHex2?|zU$sW(LlD81;({S zE79Y#qkw(Vi|Z**-7L1wUJRIweA=;pKlv0_OQj>nYW3bbA{ez6*4S`g`JPqPE>h*z zXe$@DRfx)K!5zE?VZ)mjxt&sTNmPk^k+g*L&a&X8xmWZ2ft@@Xac2RzdKcvDXfs*L zIY&${A!s0AV&$eAQR>a;_jTsQbM6H|W=}tObtruob@zJoH({Sa120vvHXfKwf3we} z<_n?ymRr#W>dTCu~Fr&wY037gg9AFB;L&Gs#t8@~xcu z`kvFK-y+#5an=?`pwB9=p>`@35p;5E^y*GS`5j+J5%0}V*Y@pj8Ufc4RbktiTBl@b zXj5_1`DQTo&t8Fn(JCakptzWa+qm|5h0VaYRia5=7*D;Tudi?YZvB?)faJ-e8TA>G zkKq+v68SqXLWN+AzCX4^8_%47Hkm!N>8I~Y6?I{HF-IzSioX{!KUB6C;j%HDD|U4_ zVxQ;eAc8(!azmPUY$O=H;uvejpf3cix^Etxoa`O#rK9Y}`--eBwhH+U8hm5g&zn5r zv*k?f`rC#N%*m_-_bJ1rrx!E(Q5;6x+chKGQYt?9P5GJW&DF{aJKnQHBYJ}D*7oL` z)aGdK7M~=HqY=~saA#KiZOYSc5z3hgv1}-Kh{J?x zCnk^-U7NyMH=z&utJ+JyM#4zlR>1o^DXrEsQ}3NcuDr@_UY*wZuIJg7t$!ORlqb zL{x`4FEU(WaMC>lXx4wM&lYw!$_hQP9Q*m<@q*xSASA~<8&opI;laz;`ZOK$quT4h zI)YwQ)10(NzQa~)*3eHpB9aKEmtLh9b44@0lo*`79}@%| zy$(1KZ(*jIz>7vEDs4y7t03tg1lsl)Q{^vbz1Grlt7xXHA=6sbnp7QaKbj3@y|;?$ zQLRME_VV40@6R84C&4#H@)PqS8|miSsZEWO%SpiM68k}UYH5as(iCt)-EPhCh5Edk zkTqiP#FM^^GCrx@?d`btU5>t6M)ixKoIr9|>RV0^-lhrpC@8xJ#at-+k@^e~sKq9E z&XtNHaYW8D=9 zr8lQx*xMUZxfUG%C60#S{&#ZEctTuLmCJ`#ySCQcDbxjdtQAcYdr^F_wUbZO%H}V) zJTrXueC8Y0z%*2WEvyvH=VK+Vi|^TYtMGWY=M4IMXVt$<=`-Cw-n;^`F|afAYshb% zf%!OX(9I1E4VC>nWu1)E75>PEvs28g@y@Zks7qlX=?4dy^?OYh$A@F@5CRa@RFSg7 zg&S%I-WCk^wG0C3ws@xcLAM4@C3H5e3kZXND(~L;0$T;C% zZ=E@4z=!bXw*>}|8FJbl*=XvU{w-(;#+3wF&;1=tTJV*-ZlQjeLO9;VXT9Y+sBX+* zATM8D4yJwL9eag&i}9+|)>$WJk)93JYR!d5;hFy7(1?!hUQa{^r;*ZvPJM|_iqzML zqk6~BI@(Vcbgq4QOpXI4xr|{(!G$05iVu&BXw~l*8rL}wtiI#B_L4CRTk;L@^^$N* zA9pQhkL80)(q-onm9aULO0#)Z#jsc_?aX*()(#iyl9w=^@fEl*NOk1Kq1l1sAfXZS zZpj|Cr+JE3YhK4C4SXRi2$>-rxIG|5JKbyLXWw#qB294BTvpS3wIg=1U7>u`t@GP%f8Tk*T`Xy|~o7{njg2b7O%{wT&g+v_K+SPiaaHl}Oh(*Si0JDx_ zO*~$a%S^<P?sEDYs;xp`4%|;-C*6RE(4AajmP7)gO_2{;AW}g?_4?+}x zG~eDOfYY9`DlK?9MzW{AZUut|F3NTR59y;id5(OdbHGyME~!+3lWxm?e;qF9bEKy9 zTk;0P!HaIhS2p`?ZaMfr;ftobKjnb!76m4hExd$ zSYMaGsCFAYa-iD1$ltYltv3<|DEdJ9r3$?y5D0P$lTKoOa8sg$XbMf6o9Xu`Mlg*&= z1I^~0g$$A?1UCMR+!oYH5VM21IwfW0sYxoFB2G8;)TzL8%uGtMvuLCVd8Ajb!Gfao z3IESdsI^;|%Fj}~n3M4fJoKF=T-5{}&8rCD&I%bpRpjn0KN}DYpS;2rvLy^N@sNq3 z)fIp;MK1jwkrH9lH1}jzo6q#7NEoOnSH`Qn>=)qpaqmfar`Mv~pehN|a$E1!F^&&j zHh68EqC%t#o6%d%bFt<^Rua)vy`%9y{)t8-+PgJcyB}VU^hYZ&eo=q3NLdxki_f6P zO!dY|$iN0loa|Fnvo+N)fY;nFFeq6;LnFcK@)DePdD4+t#8yq0ly%f;m8-9?Fm@@x zrL&N^bY3fG$jJfX-L}^5L2xp-ZcB;`h;0@6MrPj!PSXUq&O);{n#z1ceWg6i{PCv* zC!q$1258saZIyPr+j@ptHk%<=vlwQgt+ou<{SzIM60jhsWVrHW9lr5LlNI;G3lHvd z0nZgb@jQrnFW$u$;Q|G`66eZb25BADH$GO~ka83n-1Cs5#foI*oAF2AzV8b<4Gz~} zddfkUjs`ej-$++N8r}NThNYRXNlqNKWS%)m zLYcpMy+aMfsIL^K*8naa-UA|dU0 zjp6O1=SpVhVWfwkQI+W!*V`VsP7JQP;Mo5ALN4pkH98Y_B>ilc9vT&VS}AWm+BGY# z!sH}GiI20V>OMcIbN7|5qn(d#DG1i=wMy{iEpTh@@0hXoanpO{?YrUn5JBQgxp`C)D`6@^E+krijZn)`pED-%@T!ocQl~c(R+NT zr^dZ9bM4TBrkaRLDO;^ayCrOXdoY1I7E{VqbI81%Qf2;v??KD0L(C_yx&_(m> zBbh@Q$7HJ0yz>boc|qI}@2zvX>;wtR{I(M>1YKiNxcF2{@nA@tcYUw2pkdkU(t!wj zAw9Y&<(`z~ubAG=F$^0K3mWZ{(y!ixxWnaptM9)t9Nzgvy=v8D8QnnyOMG6kJF~E!1YLyi zf7*Tjt+0)!d&@s*qi*P9#AEBJumVW}V%nX}UzbVTb}ml79-`W*KpFSX1vRt7ye{aM zw7fXrfX4Ig)YNh5xWWSzMRAbjch8yawxpl^HK+|Y*skyS1}*QNfYk zX5S{Fc2GQ-DNgQ5g?pNi82-KIvj?j%eb`KO)ED5kctfgTkjn8xOG`>NR7F0y=M^YB z(rxVO6FivyW77yGFx~4vNXPQPA##db;TG`6fcp9ori%0oGEXY`?Q>j%q6<~|=>(^? zvM%4%+wXtjP^X)L6aR4m08ENMKfh+H>MtVIy=}N$9^!wd`}5R;N^o=HP~GvYarh)S z8|jxcRv#kJ?O4%is{eq-To}zDx0BJT>VSPl4HG8&goA1Pe?V^!TRN&g*|KTbG&$DJ ze=3$&aZ?tiQwQ55NxA52TTcK4K+OF2)n=3V9-FvUKdnpl4;$!qQ(KPqm zh-1YRs8%eKSZ{yFXUj&`v~yTx&w@^7VV;5)du-Mff%Gf&#%__W$|q*U(mLG+U_U(I=Ig)?y1WzAOJ%hy!I?f(nka`A~>V>@TdbP0^qL1ZHV)H=1_4*y5T5nkK?}h1Y1vv z%~rAMaI%k!A?A6WSH!SpAx^N`Yr!AbxDld+pQt%K#R6p_faChJf`y=Y2l18vn=tTNg3mV^5?Y2HzJ)o@#>wJMaE{ zM?!Ef0w}85G7^@Bgk1AyIgivO`*xMd`m0uXZ+R0sE)y!o0 z0G2@?x3%5Ir3gJfU??>B8k?ayrb0C!ac{((yPl^rd8ksfs@D_1ebXbTA&d6Gw!fwX zTpf1PyVJ2vNU91#zCYC)Dc*CmODTHQyM64p@@jmxYKqk9$k83i=<}rhg$R%PAvWwj zP-)1@oC*@^6Ez>W)KZ`{%px=*pm)~*GxaSZB~@sy<1B7%6-|$n88g>jGuXxY*!BXGazQ+b=bGcA@-YYr>q;s>a>J> zv~OjCfW0Gch2(6=A>CfLLRql5i+pCJPrkSW<#t^3`W?Z-b^B~w-d?2_LU2~YsM+7- zmsCHC16Un*?*KZ)vA2jwc654-dN%540@_P` zapn$@T=K}ArKXjqRd^F9qG%{4Nmbhp>jwI@}3XrmAHFR^WZNoQNkzQ`TZ&U3d1uCcP6$$!RQoAsdc|mn{9BL_I4~|Lymy6uImRC z5T*3274SZ$b_;^=?PnHvh|q;r8rXDd$qzclZ7-P z$eqvHT0@Jc{DPJB)60iMAVI)|jRkS0w<^Etv0aKSGz|@LFZb>uaRC73ojIijNL}_? z36S(r8VJvMX7`J((H9xjsOg!w#WVUI4tXcr+NNoAQn@W#0dkAM`tL9k!V(dDo*PCC zQ4z0YGKU&`=OZvFO@!AJdV&ko6 ztWJ2o37fhfwj$}MJ<~CP6Fq0>@*bWMIck%f$VlIdYB@fy{n`2Xl7`)S<(e61W!uwq zRLZ{?=Iy&>XdZcbozYQFa?c~m95$TfU&IUgNL9$n+mf}z)t;__g|Tz2<0n5E^;hQu zNCirapD!dqn~$gL$HvC2+=4AK&63V$rMh1DHd~ctQ#*b&UGOR+r=qq=_gYQAO%R&) z3^c%Z(tMwIIZ*dXk}GfxQ#8Sh3TDz6H9`}@kY8_SnG$#crbZb4HiroA&4ln91?RP1 z)RY#gh+CtGqkiLwPZzwTOzU00s7YTPmN%>M`dtQe0^kB>H%gEWNAvX{u#s~)4+%k3 zK-G_RP)Ui(GeW{&aum(gyV{o`=)C-WFx>bffL=BvztU{DE}@z_L*v;NkT!CQlWfBzALh6?}q9(4o)DgjXChpLUWI}?U2N8~y zs$`!8P==p{@ix^~-n-*@_STLsmXW^mU!*wRLHHN|^FCD`wp^Z63`iLn9@rEwC?|9_ zjc-8sJyn}o2bpm;0e4eCWKO{wxvY$1uH_NWzc6 z$gWspVW4qr8i5PN}j3;4$7;m+0d# zDNM;EZ)7*OECHqC%9McB)m1`@e6hm9tjD~s&&QzIXt+=yv6#5H^485h$K~^_lKGKf z)9*UT*O}ILLlAWD$ejP}FijRO7tZ&Xl;i z(gJKBFL6r*8K-U`feGCvweJc}>ZHhWb8Gqd3jYVc3kf{)fN{(V%k%4MT6&um>50rZ z;BUeN$sscFfJ{08p%wZRxL^TBM6kJjLhg*VLltO`$n^BFZFj$qK3;x8?`T|&Xj|}X z&CY$4#L$XrUAj{TP41VrOvMv^Z#D}(jzB#g0I_V;tnRy8~Ed(2#C6D@o*NVM~o_3AE*5`?S(7*z}l=zPz z%3}b2)SrT4u1=E6kNExMXYiN4^E!M@N+;6(*fOJGzwH^N87C?mrC*CO( z3ZypHTbuu&`Tj{=AE%$}T9M-;t-2N~%CR5;Pzmj`IGD^KprRpx;<}J~L~waVM0}o> zq#_#sc@?N+#_$V_adRco=F6nHYVlws09P##YYND1L26O_-suA!kqkx@{@6QNM_~-S z)em++Ck^Gs)}kNp2)KRiTkyiC#uv8qEb|^Wu-S1rwwSmH(RUxTZ_vamNktqq-%L7+ z+&VNzeDZ*r~DKUu@6C4!$bWeAV`t z?+HCsh_%DYBG>IDz~rTQpyEe`Eu8Eqi5A;(ccA=|Tfw$<1pxx)8?iwloPTTS4y>{F zQOWyQ#je{*`UzRIFg@Grg)<;0=yA?mh<;qlU@?F}Q~dC4|71A60+c`I zwxZIWZJl3cl^9po4c~-J>~pfYhz{~hl^%J=_fsEE>kh15A5bTN+p#1#lf6p-(hkEW zx2kk)81g&z1m?qapYK6ejkGZ8W5i*<@&Th}2Vsk#XG@5eNo43}O>t;f*6FagF7i(eKne&AFSyM=FrY-OM@nU8^~5^Sra$SC)kW9$jE^vApH`HBk* zFquo9dN5-;IWtDTb1WvLl5QH4E zR9`y&6P&jkekD571b={ak#fg2nDlpLcFVUI*? z=|MU`7}2@Lv3oZq-R=0OL*-mNJik9~gEaB;wt&kd2^ek3jno(E;Qx#N>qC&7+*3VpCTF)qzw^314B zJViNzNAH1LUWqwnw)Y=YB!*bAqK>VxaS2_gp2iaMY6Wo&dDA)+Gwr2B^HID0nX6la z*s5Uz@hG_o>icHD-6%6 zlqz4WFgr(H2znBaRP2-IvlGMt)D-2{u5f<#{3BA<3kf*RQbl*=9ZD0`1}PIQqiLj= z_bx41v+(*iyl@5|JB^V3YAC)ZHo@oXu%|%!#M;f^LswxRd$_$OuDxx+4Gx$cy*bj` zQo8CtQE9ST%jB|6s#i~FDQ2R2@vIqdCu7%%UyNYlDL_1Y?O5~=u|fEFGCzp4Y^RS7 zQ=G*Hz1NsTdN9dMJNFj3A3h(HDHc#}-Te9Q(6buVA0OGCq(F$T8vwb1{X51FJ z6$!qyRuf0YzAqL~p)iSvxb@zhw4>_bubW1{=|x4jF8ZBHI_vJ&?%uMB>tctk#}iI3 zpUyGu_QrG-JmBBv7zJOPjxVWJv zaU(7cj(zyb*#=g>Uztuf)Q4@NpKGv0+BM%+3&E$66aPh2T3
    fNWLff#kfC93Yy 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 -- Gitee From c9af7150e155bbd67b0df7b99d4828387acaee00 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 18:20:02 +0800 Subject: [PATCH 53/54] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52a6ea3d..bf5035be 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ​ dorive是一个轻量的领域驱动式业务框架,它提供了诸多开箱即用的功能,旨在帮助开发者快速、便捷地在项目中应用领域驱动,并从中受益。 -​ 这些功能涵盖了依赖注入校验、依赖即用配置、实体定义与映射、级联查询与操作、实体多态、实体事件通知、复杂推导查询、ref关键字、复杂计数统计、表结构生成、数据库逆向生成、代码生成等,覆盖了大部分开发场景。 +​ 这些功能涵盖了依赖注入校验、依赖即用配置、实体定义与映射、级联查询与操作、实体多态、实体事件通知、复杂推导查询、ref关键字、复杂计数统计、表结构生成、数据库逆向生成、接口代码生成等,覆盖了大部分开发场景。 ### 🎁名称由来 -- Gitee From 01522620842153dcc92183e17798ab340196ba75 Mon Sep 17 00:00:00 2001 From: "tao.chen1" Date: Thu, 21 Mar 2024 18:25:20 +0800 Subject: [PATCH 54/54] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bf5035be..b0e807e8 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ stars


    + ### 📚简介 ​ dorive是一个轻量的领域驱动式业务框架,它提供了诸多开箱即用的功能,旨在帮助开发者快速、便捷地在项目中应用领域驱动,并从中受益。 -- Gitee