From d84030494c6414e0a1451209755e5705df3d910d Mon Sep 17 00:00:00 2001 From: wangyu <727842003@qq.com> Date: Fri, 28 Jun 2024 11:06:53 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E9=9D=9E=E5=B8=B8?= =?UTF-8?q?=E7=89=9B=E9=80=BC=E7=9A=84=E5=8A=A8=E6=80=81=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E5=92=8C=E5=85=B3=E8=81=94=E6=84=9F=E7=9F=A5=EF=BC=8C=E5=B0=BD?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E4=BD=BF=E7=94=A8=E5=8E=9F=E7=94=9F=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/base/DomainRepository.java | 10 -- .../annotations/relation/Association.java | 6 +- .../impl/DefaultReactiveRepositoryImpl.java | 8 - .../r2dbc/config/R2dbcDataConfig.java | 9 +- .../config/annotations/EnableR2dbcRepo.java | 4 +- .../callback/ReferenceR2dbcCallback.java | 69 +++++--- .../r2dbc/metadata/R2dbcTableMetadata.java | 23 +-- .../impl/SimpleR2dbcMetadataManager.java | 150 +++++++++++++----- .../r2dbc/metadata/reference/FieldSetter.java | 8 + .../metadata/reference/R2dbcAssociation.java | 33 ++-- .../metadata/reference/R2dbcCollection.java | 20 ++- .../DefaultReactiveRepositoryFactory.java | 6 +- ...a => QueryingR2dbcReactiveRepository.java} | 18 +-- .../framework/r2dbc/R2DbcRepositoryTest.java | 7 +- .../framework/r2dbc/domain/TestDO.java | 14 +- .../r2dbc/domain/reference/TestChild.java | 3 + .../r2dbc/domain/reference/TestOther.java | 3 +- .../src/test/resources/schema-clean.sql | 1 + 18 files changed, 248 insertions(+), 144 deletions(-) create mode 100644 flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/FieldSetter.java rename flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/impl/{DefaultReactiveRepositoryImpl.java => QueryingR2dbcReactiveRepository.java} (91%) diff --git a/flyfish-data/flyfish-data-common/src/main/java/com/flyfish/framework/repository/base/DomainRepository.java b/flyfish-data/flyfish-data-common/src/main/java/com/flyfish/framework/repository/base/DomainRepository.java index 19095fc..348babc 100644 --- a/flyfish-data/flyfish-data-common/src/main/java/com/flyfish/framework/repository/base/DomainRepository.java +++ b/flyfish-data/flyfish-data-common/src/main/java/com/flyfish/framework/repository/base/DomainRepository.java @@ -1,7 +1,5 @@ package com.flyfish.framework.repository.base; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.repository.core.EntityInformation; /** @@ -17,12 +15,4 @@ public interface DomainRepository { * @return 结果 */ EntityInformation getEntityInformation(); - - /** - * 获取持久化实体信息 - * - * @param

持久化属性泛型 - * @return 结果 - */ -

> PersistentEntity getPersistentEntity(); } diff --git a/flyfish-data/flyfish-data-domain/src/main/java/com/flyfish/framework/annotations/relation/Association.java b/flyfish-data/flyfish-data-domain/src/main/java/com/flyfish/framework/annotations/relation/Association.java index ee99ecf..bb80e2f 100644 --- a/flyfish-data/flyfish-data-domain/src/main/java/com/flyfish/framework/annotations/relation/Association.java +++ b/flyfish-data/flyfish-data-domain/src/main/java/com/flyfish/framework/annotations/relation/Association.java @@ -1,6 +1,5 @@ package com.flyfish.framework.annotations.relation; -import org.springframework.data.annotation.Reference; import org.springframework.data.annotation.Transient; import java.lang.annotation.*; @@ -14,17 +13,20 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Documented @Transient -@Reference public @interface Association { /** * @return 关联在本对象上的字段名称 + * 可不指定。不指定时,会自动取得当前对象上 @Reference 注解的字段 + *

* 如当前表的detail_id字段对应detail表的id字段,则该值为 detail_id */ String field() default ""; /** * @return 关联在外部对象上的字段名称,注意,禁止循环依赖 + * 可不指定。不指定时,会自动取得目标类型中 @Reference 注解的字段 + *

* 如当前表不存在目标表detail的关联id,目标表关联本实体的id为main_id,则该值为main_id。 * 与field互斥,同时指定永远都以field为主 */ diff --git a/flyfish-data/flyfish-data-mongodb/src/main/java/com/flyfish/framework/mongodb/repository/impl/DefaultReactiveRepositoryImpl.java b/flyfish-data/flyfish-data-mongodb/src/main/java/com/flyfish/framework/mongodb/repository/impl/DefaultReactiveRepositoryImpl.java index 5981ca0..69771e4 100644 --- a/flyfish-data/flyfish-data-mongodb/src/main/java/com/flyfish/framework/mongodb/repository/impl/DefaultReactiveRepositoryImpl.java +++ b/flyfish-data/flyfish-data-mongodb/src/main/java/com/flyfish/framework/mongodb/repository/impl/DefaultReactiveRepositoryImpl.java @@ -10,10 +10,7 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; -import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.mapping.PersistentProperty; import org.springframework.data.mongodb.core.ReactiveMongoOperations; -import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.query.MongoEntityInformation; @@ -188,9 +185,4 @@ public class DefaultReactiveRepositoryImpl extends SimpleReact private Optional getQuery(Qo qo) { return qo.getQuery(entityInformation); } - - @Override - public

> PersistentEntity getPersistentEntity() { - return null; - } } diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/R2dbcDataConfig.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/R2dbcDataConfig.java index 0175bd5..e0d1dc8 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/R2dbcDataConfig.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/R2dbcDataConfig.java @@ -6,7 +6,7 @@ import com.flyfish.framework.r2dbc.metadata.R2dbcMetadataManager; import com.flyfish.framework.r2dbc.metadata.impl.SimpleR2dbcMetadataManager; import com.flyfish.framework.r2dbc.operations.R2dbcReactiveEntityOperations; import com.flyfish.framework.r2dbc.repository.factory.DefaultReactiveRepositoryFactoryBean; -import com.flyfish.framework.r2dbc.repository.impl.DefaultReactiveRepositoryImpl; +import com.flyfish.framework.r2dbc.repository.impl.QueryingR2dbcReactiveRepository; import com.flyfish.framework.repository.ReactiveEntityOperations; import io.r2dbc.spi.ConnectionFactory; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -16,6 +16,7 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.data.mapping.callback.EntityCallback; import org.springframework.data.r2dbc.config.EnableR2dbcAuditing; import org.springframework.data.r2dbc.core.R2dbcEntityOperations; +import org.springframework.data.r2dbc.mapping.R2dbcMappingContext; import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; import org.springframework.r2dbc.connection.init.CompositeDatabasePopulator; import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer; @@ -31,7 +32,7 @@ import java.util.Optional; */ @EnableR2dbcRepositories( repositoryFactoryBeanClass = DefaultReactiveRepositoryFactoryBean.class, - repositoryBaseClass = DefaultReactiveRepositoryImpl.class, + repositoryBaseClass = QueryingR2dbcReactiveRepository.class, basePackages = "com.flyfish.framework.*.repository" ) @EnableR2dbcAuditing @@ -49,8 +50,8 @@ public class R2dbcDataConfig { } @Bean - public R2dbcMetadataManager r2dbcMetadataManager() { - return new SimpleR2dbcMetadataManager(); + public R2dbcMetadataManager r2dbcMetadataManager(R2dbcMappingContext mappingContext) { + return new SimpleR2dbcMetadataManager(mappingContext); } /** diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/annotations/EnableR2dbcRepo.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/annotations/EnableR2dbcRepo.java index 0a652d4..0710713 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/annotations/EnableR2dbcRepo.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/annotations/EnableR2dbcRepo.java @@ -1,7 +1,7 @@ package com.flyfish.framework.r2dbc.config.annotations; import com.flyfish.framework.r2dbc.repository.factory.DefaultReactiveRepositoryFactoryBean; -import com.flyfish.framework.r2dbc.repository.impl.DefaultReactiveRepositoryImpl; +import com.flyfish.framework.r2dbc.repository.impl.QueryingR2dbcReactiveRepository; import org.springframework.core.annotation.AliasFor; import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; @@ -14,7 +14,7 @@ import java.lang.annotation.*; */ @EnableR2dbcRepositories( repositoryFactoryBeanClass = DefaultReactiveRepositoryFactoryBean.class, - repositoryBaseClass = DefaultReactiveRepositoryImpl.class + repositoryBaseClass = QueryingR2dbcReactiveRepository.class ) @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/callback/ReferenceR2dbcCallback.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/callback/ReferenceR2dbcCallback.java index 81b4396..e72aa47 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/callback/ReferenceR2dbcCallback.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/config/callback/ReferenceR2dbcCallback.java @@ -25,7 +25,10 @@ import org.springframework.data.util.Lazy; import org.springframework.lang.NonNull; import reactor.core.publisher.Mono; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; @@ -61,14 +64,14 @@ public class ReferenceR2dbcCallback implements AfterConvertCallback { // 遍历fields,找到要注入的数据 R2dbcTableMetadata metadata = r2dbcMetadataManager.getMetadata(entityClass); // 获取本实体的持久化对象 - PersistentEntity persistentEntity = getRepository(entityClass).getPersistentEntity(); + PersistentEntity persistentEntity = metadata.getEntity(); // 获取属性设置器 PersistentPropertyAccessor accessor = persistentEntity.getPropertyAccessor(entity); // 尝试填充一对一关联 List> signals = new ArrayList<>(); - signals.addAll(fetchAssociation(metadata.getAssociations(), persistentEntity, accessor)); - signals.addAll(fetchCollections(metadata.getCollections(), persistentEntity, accessor)); + signals.addAll(fetchAssociation(metadata.getAssociations(), accessor)); + signals.addAll(fetchCollections(metadata.getCollections(), accessor)); // 尝试填充一对多关联 if (CollectionUtils.isNotEmpty(signals)) { @@ -87,24 +90,26 @@ public class ReferenceR2dbcCallback implements AfterConvertCallback { .collect(Collectors.toMap(repo -> repo.getEntityInformation().getJavaType(), Function.identity()))); } - private List> fetchAssociation(List associations, PersistentEntity persistentEntity, - PersistentPropertyAccessor accessor) { + /** + * 拉取一对一关联 + * + * @param associations 一对一关联集合 + * @param accessor 本对象的属性访问器 + * @return 结果 + */ + private List> fetchAssociation(List associations, PersistentPropertyAccessor accessor) { List> signals = new ArrayList<>(); if (CollectionUtils.isNotEmpty(associations)) { // 分区,将判断提在外面,提升性能 Map> partitions = associations.stream() - .filter(R2dbcAssociation::isValid) .collect(Collectors.partitioningBy(R2dbcAssociation::isInner)); // 处理内部查询 if (CollectionUtils.isNotEmpty(partitions.get(true))) { partitions.get(true).forEach(association -> { // 获取对方的仓库 - DefaultReactiveRepository repository = getRepository(association.getEntityClass()); + DefaultReactiveRepository repository = getRepository(association.getEntity().getType()); // 内部查询,以对象内的值作为查询条件 - PersistentProperty property = persistentEntity.getPersistentProperty(association.getField()); - if (null == property) return; - // 得到条件值 - Object value = accessor.getProperty(property); + Object value = accessor.getProperty(association.getField()); if (ObjectUtils.isEmpty(value)) return; // 查询 Mono signal = repository.findById(String.valueOf(value)) @@ -120,12 +125,9 @@ public class ReferenceR2dbcCallback implements AfterConvertCallback { // 获取条件 String id = accessor.getBean().getId(); // 准备查询 - DefaultReactiveRepository repository = getRepository(association.getEntityClass()); - // 获取目标属性名 - PersistentProperty property = repository.getPersistentEntity().getPersistentProperty(association.getField()); - if (null == property) return; + DefaultReactiveRepository repository = getRepository(association.getEntity().getType()); // 直接通过外部字段查询 - Mono signal = repository.findOne(Queries.where(getColumnName(property)).eq(id).wrap()) + Mono signal = repository.findOne(Queries.where(getColumnName(association.getField())).eq(id).wrap()) .map(result -> association.setValue(accessor.getBean(), result)) .defaultIfEmpty(accessor.getBean()); signals.add(signal); @@ -135,11 +137,40 @@ public class ReferenceR2dbcCallback implements AfterConvertCallback { return signals; } - private List> fetchCollections(List collections, PersistentEntity persistentEntity, + /** + * 拉取一对多关联实体 + * + * @param collections 一对多关联 + * @param accessor 本对象的属性访问器 + * @return 结果 + */ + private List> fetchCollections(List collections, PersistentPropertyAccessor accessor) { - return Collections.emptyList(); + List> signals = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(collections)) { + // 外部查询,以当前实体的id为条件,去外部表的字段上查询 + collections.forEach(collection -> { + // 获取条件 + String id = accessor.getBean().getId(); + // 准备查询 + DefaultReactiveRepository repository = getRepository(collection.getEntity().getType()); + // 直接通过外部字段查询 + Mono signal = repository.findAll(Queries.where(getColumnName(collection.getField())).eq(id).wrap()) + .collectList() + .map(result -> collection.setValue(accessor.getBean(), result)) + .defaultIfEmpty(accessor.getBean()); + signals.add(signal); + }); + } + return signals; } + /** + * 通过持久化属性获取真实的列名 + * + * @param property 属性 + * @return 结果 + */ private String getColumnName(PersistentProperty property) { if (property instanceof RelationalPersistentProperty) { return ((RelationalPersistentProperty) property).getColumnName().getReference(); diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/R2dbcTableMetadata.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/R2dbcTableMetadata.java index 09a8cad..f371dbb 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/R2dbcTableMetadata.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/R2dbcTableMetadata.java @@ -1,10 +1,12 @@ package com.flyfish.framework.r2dbc.metadata; import com.flyfish.framework.annotations.Property; -import com.flyfish.framework.domain.base.Domain; +import com.flyfish.framework.r2dbc.metadata.reference.FieldSetter; import com.flyfish.framework.r2dbc.metadata.reference.R2dbcAssociation; import com.flyfish.framework.r2dbc.metadata.reference.R2dbcCollection; import lombok.Data; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; import java.util.ArrayList; import java.util.List; @@ -18,11 +20,8 @@ import java.util.List; @Property("表的元数据,用于关联查询") public class R2dbcTableMetadata { - @Property("表名") - private String tableName; - - @Property("表实体类名") - private Class entityClass; + @Property("表实体信息") + private PersistentEntity entity; @Property("一对一关联信息") private List associations = new ArrayList<>(); @@ -30,12 +29,16 @@ public class R2dbcTableMetadata { @Property("一对多关联信息") private List collections = new ArrayList<>(); - public R2dbcTableMetadata(Class entityClass) { - this.entityClass = entityClass; + public R2dbcTableMetadata(PersistentEntity entity) { + this.entity = entity; } - public void addAssociation(org.springframework.data.mapping.model.Property property, String field, boolean inner) { - this.associations.add(R2dbcAssociation.of(property, field, inner)); + public void addAssociation(PersistentEntity entity, PersistentProperty property, FieldSetter setter, boolean inner) { + this.associations.add(R2dbcAssociation.of(entity, property, setter, inner)); + } + + public void addCollection(PersistentEntity entity, PersistentProperty property, FieldSetter setter) { + this.collections.add(R2dbcCollection.of(entity, property, setter)); } } diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/impl/SimpleR2dbcMetadataManager.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/impl/SimpleR2dbcMetadataManager.java index 19aaba0..914a467 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/impl/SimpleR2dbcMetadataManager.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/impl/SimpleR2dbcMetadataManager.java @@ -4,24 +4,28 @@ import com.flyfish.framework.annotations.relation.Association; import com.flyfish.framework.domain.base.Domain; import com.flyfish.framework.r2dbc.metadata.R2dbcMetadataManager; import com.flyfish.framework.r2dbc.metadata.R2dbcTableMetadata; +import com.flyfish.framework.r2dbc.metadata.reference.FieldSetter; +import lombok.RequiredArgsConstructor; import org.apache.commons.lang3.ClassUtils; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.reflect.FieldUtils; import org.springframework.beans.BeanUtils; +import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.data.mapping.model.Property; -import org.springframework.data.util.ClassTypeInformation; -import org.springframework.lang.NonNull; -import org.springframework.util.ReflectionUtils; +import org.springframework.data.annotation.Reference; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.r2dbc.mapping.R2dbcMappingContext; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; -import java.util.Arrays; +import java.lang.reflect.Method; import java.util.Collection; -import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.function.Function; -import java.util.stream.Collectors; +import java.util.function.Consumer; +import java.util.stream.StreamSupport; /** * 简单的元数据管理器 @@ -29,8 +33,10 @@ import java.util.stream.Collectors; * @author wangyu * 基于线程安全的反射方法进行加载并持有 */ +@RequiredArgsConstructor public class SimpleR2dbcMetadataManager implements R2dbcMetadataManager { + private final R2dbcMappingContext r2dbcMappingContext; private final ConcurrentMap, R2dbcTableMetadata> METADATA_STORE = new ConcurrentHashMap<>(); /** @@ -51,55 +57,121 @@ public class SimpleR2dbcMetadataManager implements R2dbcMetadataManager { * @return 结果 */ private R2dbcTableMetadata computeMetadata(Class entityClass) { - MetadataCreator creator = new MetadataCreator(entityClass); - ReflectionUtils.doWithFields(entityClass, creator); + PersistentEntity entity = r2dbcMappingContext.getRequiredPersistentEntity(entityClass); + MetadataCreator creator = new MetadataCreator(entity); + FieldUtils.getFieldsListWithAnnotation(entityClass, Association.class).forEach(creator); return creator.metadata; } - private static class MetadataCreator implements ReflectionUtils.FieldCallback { + private class MetadataCreator implements Consumer { - private final Map descriptors; - - private final ClassTypeInformation typeInformation; + private final PersistentEntity entity; private final R2dbcTableMetadata metadata; - private MetadataCreator(Class entityClass) { - this.descriptors = Arrays.stream(BeanUtils.getPropertyDescriptors(entityClass)) - .collect(Collectors.toMap(PropertyDescriptor::getName, Function.identity())); - this.typeInformation = ClassTypeInformation.from(entityClass); - this.metadata = new R2dbcTableMetadata(entityClass); + private MetadataCreator(PersistentEntity entity) { + this.entity = entity; + this.metadata = new R2dbcTableMetadata(entity); + } + + private void invokeSetter(Method method, Object obj, Object value) { + try { + method.invoke(obj, value); + } catch (Exception ignored) { + + } + } + + private FieldSetter getSetter(Field field) { + PropertyDescriptor descriptor = BeanUtils.getPropertyDescriptor(entity.getType(), field.getName()); + return Optional.ofNullable(descriptor).map(PropertyDescriptor::getWriteMethod) + .map(method -> (FieldSetter) (obj, value) -> invokeSetter(method, obj, value)) + .orElse(null); } /** - * Perform an operation using the given field. + * 处理所有关联属性 * - * @param field the field to operate on + * @param field 属性永远都不为空 */ @Override - public void doWith(@NonNull Field field) throws IllegalArgumentException, IllegalAccessException { - if (!descriptors.containsKey(field.getName())) return; - Property property = Property.of(typeInformation, field, descriptors.get(field.getName())); - Class fieldType = property.getType(); - - ReflectionUtils.makeAccessible(field); - + public void accept(Field field) { Association association = AnnotatedElementUtils.findMergedAnnotation(field, Association.class); - if (null != association) { - if (ClassUtils.isAssignable(fieldType, Domain.class)) { - // 一对一关联 - if (StringUtils.isNotBlank(association.field())) { - metadata.addAssociation(property, association.field(), true); - } else if (StringUtils.isNotBlank(association.foreignField())) { - metadata.addAssociation(property, association.foreignField(), false); + if (null == association) return; + FieldSetter setter = getSetter(field); + if (null == setter) return; + // 获取具体类型 + Class type = field.getType(); + if (ClassUtils.isAssignable(type, Domain.class)) { + // 目标类型 + PersistentEntity targetEntity = r2dbcMappingContext.getRequiredPersistentEntity(type); + // 一对一关联 + if (StringUtils.isNotBlank(association.field())) { + // 获取内部属性 + PersistentProperty property = entity.getRequiredPersistentProperty(association.field()); + // 添加内部关联 + metadata.addAssociation(targetEntity, property, setter, true); + } else if (StringUtils.isNotBlank(association.foreignField())) { + // 获取外部属性 + PersistentProperty property = targetEntity.getRequiredPersistentProperty(association.foreignField()); + // 添加外部关联 + metadata.addAssociation(targetEntity, property, setter, false); + } else { + // 都为空,从当前对象查找目标类型的reference,并自动添加关联 + Optional> inner = findInnerKey(type); + if (inner.isPresent()) { + metadata.addAssociation(targetEntity, inner.get(), setter, true); } else { - // 尚且不支持空白策略 + findForeignKey(targetEntity).ifPresent(property -> metadata.addAssociation(targetEntity, property, setter, false)); } - } else if (ClassUtils.isAssignable(fieldType, Collection.class)) { - // 一对多关联 - + } + } else if (ClassUtils.isAssignable(type, Collection.class)) { + // 获取真实的类型 + Class entityType = ResolvableType.forField(field).resolveGeneric(0); + if (null == entityType) return; + // 目标类型 + PersistentEntity targetEntity = r2dbcMappingContext.getRequiredPersistentEntity(entityType); + // 一对多关联,仅支持外部字段 + if (StringUtils.isNotBlank(association.foreignField())) { + // 获取外部属性 + PersistentProperty property = targetEntity.getRequiredPersistentProperty(association.foreignField()); + // 添加外部关联 + metadata.addCollection(targetEntity, property, setter); + } else { + // 都为空,从当前对象查找目标类型的reference,并自动添加关联 + findForeignKey(targetEntity).ifPresent(property -> metadata.addCollection(targetEntity, property, setter)); } } } + + /** + * 从当前实体查找目的实体的外键 + * 从当前实体找到关联目标实体的id + * + * @param entityClass 目的实体类型 + * @return 结果 + */ + private Optional> findInnerKey(Class entityClass) { + return StreamSupport.stream(entity.getPersistentProperties(Reference.class).spliterator(), false) + .filter(property -> ClassUtils.isAssignable(entityClass, property.getRequiredAnnotation(Reference.class).value())) + .findFirst() + .map(p -> (PersistentProperty) p); + } + + /** + * 查找目的实体的外键 + * 从目标实体中找到关联本实体的id + * + * @param targetEntity 目的实体信息 + * @return 结果 + */ + private Optional> findForeignKey(PersistentEntity targetEntity) { + Class ownerEntityClass = entity.getType(); + return StreamSupport.stream(targetEntity.getPersistentProperties(Reference.class).spliterator(), false) + .filter(property -> ClassUtils.isAssignable(ownerEntityClass, property.getRequiredAnnotation(Reference.class).value())) + .findFirst() + .map(p -> (PersistentProperty) p); + } + } } diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/FieldSetter.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/FieldSetter.java new file mode 100644 index 0000000..964db16 --- /dev/null +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/FieldSetter.java @@ -0,0 +1,8 @@ +package com.flyfish.framework.r2dbc.metadata.reference; + +import java.util.function.BiConsumer; + +@FunctionalInterface +public interface FieldSetter extends BiConsumer { + +} diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/R2dbcAssociation.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/R2dbcAssociation.java index 5e0a3f1..87ca241 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/R2dbcAssociation.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/R2dbcAssociation.java @@ -1,10 +1,11 @@ package com.flyfish.framework.r2dbc.metadata.reference; import com.flyfish.framework.annotations.Property; +import com.flyfish.framework.domain.base.Domain; import lombok.AllArgsConstructor; import lombok.Data; - -import java.lang.reflect.Method; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; @Data @Property("一对一关联查询") @@ -12,37 +13,23 @@ import java.lang.reflect.Method; public class R2dbcAssociation { @Property("关联实体类") - private Class entityClass; + private PersistentEntity entity; @Property("关联字段") - private String field; + private PersistentProperty field; @Property("目标字段") - private org.springframework.data.mapping.model.Property targetField; + private FieldSetter setter; @Property("字段是否在当前实体内部") boolean inner; - public static R2dbcAssociation of(org.springframework.data.mapping.model.Property targetField, String field, boolean inner) { - return new R2dbcAssociation(targetField.getType(), field, targetField, inner); + public static R2dbcAssociation of(PersistentEntity entity, PersistentProperty field, FieldSetter setter, boolean inner) { + return new R2dbcAssociation(entity, field, setter, inner); } - public boolean isValid() { - return targetField.hasAccessor(); - } - - public T setValue(T obj, Object value) { - if (null != value && targetField.hasAccessor()) { - targetField.getSetter().ifPresent(method -> invokeSetter(method, obj, value)); - } + public T setValue(T obj, Object value) { + setter.accept(obj, value); return obj; } - - private void invokeSetter(Method method, Object obj, Object value) { - try { - method.invoke(obj, value); - } catch (Exception ignored) { - - } - } } diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/R2dbcCollection.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/R2dbcCollection.java index 3f68f5f..54d6ad3 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/R2dbcCollection.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/metadata/reference/R2dbcCollection.java @@ -2,15 +2,31 @@ package com.flyfish.framework.r2dbc.metadata.reference; import com.flyfish.framework.annotations.Property; import com.flyfish.framework.domain.base.Domain; +import lombok.AllArgsConstructor; import lombok.Data; +import org.springframework.data.mapping.PersistentEntity; +import org.springframework.data.mapping.PersistentProperty; @Data @Property("一对多关联查询") +@AllArgsConstructor public class R2dbcCollection { @Property("关联实体类") - private Class entityClass; + private PersistentEntity entity; @Property("关联字段") - private String field; + private PersistentProperty field; + + @Property("目标字段") + private FieldSetter setter; + + public static R2dbcCollection of(PersistentEntity entity, PersistentProperty field, FieldSetter setter) { + return new R2dbcCollection(entity, field, setter); + } + + public T setValue(T obj, Object value) { + setter.accept(obj, value); + return obj; + } } diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/factory/DefaultReactiveRepositoryFactory.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/factory/DefaultReactiveRepositoryFactory.java index d2030eb..2467e8c 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/factory/DefaultReactiveRepositoryFactory.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/factory/DefaultReactiveRepositoryFactory.java @@ -1,6 +1,6 @@ package com.flyfish.framework.r2dbc.repository.factory; -import com.flyfish.framework.r2dbc.repository.impl.DefaultReactiveRepositoryImpl; +import com.flyfish.framework.r2dbc.repository.impl.QueryingR2dbcReactiveRepository; import org.springframework.data.r2dbc.core.R2dbcEntityOperations; import org.springframework.data.r2dbc.repository.support.R2dbcRepositoryFactory; import org.springframework.data.repository.core.RepositoryMetadata; @@ -16,12 +16,12 @@ public class DefaultReactiveRepositoryFactory extends R2dbcRepositoryFactory { */ public DefaultReactiveRepositoryFactory(R2dbcEntityOperations operations) { super(operations); - this.setRepositoryBaseClass(DefaultReactiveRepositoryImpl.class); + this.setRepositoryBaseClass(QueryingR2dbcReactiveRepository.class); } @Override @NonNull protected Class getRepositoryBaseClass(@NonNull RepositoryMetadata metadata) { - return DefaultReactiveRepositoryImpl.class; + return QueryingR2dbcReactiveRepository.class; } } diff --git a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/impl/DefaultReactiveRepositoryImpl.java b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/impl/QueryingR2dbcReactiveRepository.java similarity index 91% rename from flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/impl/DefaultReactiveRepositoryImpl.java rename to flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/impl/QueryingR2dbcReactiveRepository.java index a944e72..f9d1c4b 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/impl/DefaultReactiveRepositoryImpl.java +++ b/flyfish-data/flyfish-data-r2dbc/src/main/java/com/flyfish/framework/r2dbc/repository/impl/QueryingR2dbcReactiveRepository.java @@ -13,12 +13,10 @@ import org.springframework.data.domain.Sort; import org.springframework.data.r2dbc.convert.R2dbcConverter; import org.springframework.data.r2dbc.core.R2dbcEntityOperations; import org.springframework.data.r2dbc.repository.support.SimpleR2dbcRepository; -import org.springframework.data.relational.core.mapping.RelationalPersistentEntity; import org.springframework.data.relational.core.query.Criteria; import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.repository.query.RelationalEntityInformation; import org.springframework.data.repository.core.EntityInformation; -import org.springframework.data.util.Lazy; import org.springframework.lang.NonNull; import org.springframework.util.Assert; import reactor.core.publisher.Flux; @@ -32,7 +30,7 @@ import java.util.Optional; * * @param 泛型 */ -public class DefaultReactiveRepositoryImpl extends SimpleR2dbcRepository +public class QueryingR2dbcReactiveRepository extends SimpleR2dbcRepository implements DefaultReactiveRepository { @Getter @@ -40,8 +38,6 @@ public class DefaultReactiveRepositoryImpl extends SimpleR2dbc private final R2dbcEntityOperations entityOperations; - private final Lazy> persistentEntity; - /** * 构造新的默认仓库 * @@ -49,13 +45,11 @@ public class DefaultReactiveRepositoryImpl extends SimpleR2dbc * @param entityOperations 数据库操作模板 * @param converter 转换器 */ - public DefaultReactiveRepositoryImpl(RelationalEntityInformation entity, R2dbcEntityOperations entityOperations, R2dbcConverter converter) { + public QueryingR2dbcReactiveRepository(RelationalEntityInformation entity, R2dbcEntityOperations entityOperations, R2dbcConverter converter) { super(entity, entityOperations, converter); this.entity = entity; this.entityOperations = entityOperations; - this.persistentEntity = Lazy.of(() -> (RelationalPersistentEntity) converter - .getMappingContext() - .getRequiredPersistentEntity(entity.getJavaType())); + } /** @@ -236,12 +230,6 @@ public class DefaultReactiveRepositoryImpl extends SimpleR2dbc return entity; } - @SuppressWarnings("unchecked") - @Override - public RelationalPersistentEntity getPersistentEntity() { - return persistentEntity.get(); - } - private Mono getQuery(Qo qo) { return Mono.justOrEmpty(qo.getQuery(entity)); } diff --git a/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/R2DbcRepositoryTest.java b/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/R2DbcRepositoryTest.java index 8f6d7ab..dcafdcf 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/R2DbcRepositoryTest.java +++ b/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/R2DbcRepositoryTest.java @@ -1,6 +1,7 @@ package com.flyfish.framework.r2dbc; import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.serializer.SerializerFeature; import com.flyfish.framework.r2dbc.config.R2dbcDataConfig; import com.flyfish.framework.r2dbc.domain.TestDO; import com.flyfish.framework.r2dbc.domain.TestQO; @@ -26,7 +27,7 @@ public class R2DbcRepositoryTest { @Test public void test() { TestQO qo = new TestQO(); - qo.setName("2"); + qo.setName("1"); // 开启关联查询 qo.setFetchRef(true); @@ -36,7 +37,7 @@ public class R2DbcRepositoryTest { test.setName("测试名称"); test.setOtherId("1"); - System.out.println(JSON.toJSONString(testRepository.insert(test).block())); - System.out.println(JSON.toJSONString(testRepository.findAll(qo).collectList().block())); + System.out.println(JSON.toJSONString(testRepository.insert(test).block(), SerializerFeature.PrettyFormat)); + System.out.println(JSON.toJSONString(testRepository.findAll(qo).collectList().block(), SerializerFeature.PrettyFormat)); } } diff --git a/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/TestDO.java b/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/TestDO.java index e18e15f..8046c25 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/TestDO.java +++ b/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/TestDO.java @@ -6,24 +6,32 @@ import com.flyfish.framework.r2dbc.domain.reference.TestAsso; import com.flyfish.framework.r2dbc.domain.reference.TestChild; import com.flyfish.framework.r2dbc.domain.reference.TestOther; import lombok.Data; +import org.springframework.data.annotation.Reference; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; import java.util.List; +/** + * 测试实体 + * 体现了所有的情况 + * + * @author wangyu + */ @Table("test") @Data public class TestDO extends AuditDomain { @Column("other_id") + @Reference(TestAsso.class) private String otherId; - @Association(field = "otherId") + @Association private TestAsso asso; - @Association(foreignField = "testId") + @Association private TestOther other; - @Association(foreignField = "parentId") + @Association private List children; } diff --git a/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/reference/TestChild.java b/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/reference/TestChild.java index c3bb1de..d2b821d 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/reference/TestChild.java +++ b/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/reference/TestChild.java @@ -1,7 +1,9 @@ package com.flyfish.framework.r2dbc.domain.reference; import com.flyfish.framework.domain.base.AuditDomain; +import com.flyfish.framework.r2dbc.domain.TestDO; import lombok.Data; +import org.springframework.data.annotation.Reference; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; @@ -10,5 +12,6 @@ import org.springframework.data.relational.core.mapping.Table; public class TestChild extends AuditDomain { @Column("parent_id") + @Reference(TestDO.class) private String parentId; } diff --git a/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/reference/TestOther.java b/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/reference/TestOther.java index 2f3a9dd..8366eba 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/reference/TestOther.java +++ b/flyfish-data/flyfish-data-r2dbc/src/test/java/com/flyfish/framework/r2dbc/domain/reference/TestOther.java @@ -1,9 +1,9 @@ package com.flyfish.framework.r2dbc.domain.reference; -import com.flyfish.framework.annotations.relation.Association; import com.flyfish.framework.domain.base.AuditDomain; import com.flyfish.framework.r2dbc.domain.TestDO; import lombok.Data; +import org.springframework.data.annotation.Reference; import org.springframework.data.relational.core.mapping.Column; import org.springframework.data.relational.core.mapping.Table; @@ -12,5 +12,6 @@ import org.springframework.data.relational.core.mapping.Table; public class TestOther extends AuditDomain { @Column("test_id") + @Reference(TestDO.class) private String testId; } diff --git a/flyfish-data/flyfish-data-r2dbc/src/test/resources/schema-clean.sql b/flyfish-data/flyfish-data-r2dbc/src/test/resources/schema-clean.sql index 49c9d18..a3eb9a3 100644 --- a/flyfish-data/flyfish-data-r2dbc/src/test/resources/schema-clean.sql +++ b/flyfish-data/flyfish-data-r2dbc/src/test/resources/schema-clean.sql @@ -1,3 +1,4 @@ DROP TABLE IF EXISTS `test`; DROP TABLE IF EXISTS `test_child`; DROP TABLE IF EXISTS `test_asso`; +DROP TABLE IF EXISTS `test_other`;