diff --git a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/GroupField.java b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/GroupField.java new file mode 100755 index 0000000000000000000000000000000000000000..954e3d95f7d15d96f6a41369264087542aec3e04 --- /dev/null +++ b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/GroupField.java @@ -0,0 +1,19 @@ +package org.dromara.easyes.annotation; + + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据分组字段 + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface GroupField { + + String dateFormat() default "yyyyMM"; +} diff --git a/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Routing.java b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Routing.java new file mode 100755 index 0000000000000000000000000000000000000000..54a079d4094741c4e71d48dbe1c023d1d24eec4c --- /dev/null +++ b/easy-es-annotation/src/main/java/org/dromara/easyes/annotation/Routing.java @@ -0,0 +1,22 @@ +package org.dromara.easyes.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 路由字段 + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) +public @interface Routing { + + /** + * defines how the routing is determined. Can be either the name of a property or a SpEL expression. See the reference + * documentation for examples how to use this annotation. + */ + String value(); +} diff --git a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/ClassPathMapperScanner.java b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/ClassPathMapperScanner.java index 02d29a5a606bc2471b594402465f65dc8650a0cd..9de8f7e537451feb110ca937e29a96488e7a127f 100644 --- a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/ClassPathMapperScanner.java +++ b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/ClassPathMapperScanner.java @@ -18,8 +18,11 @@ import java.util.Set; * Copyright © 2021 xpc1024 All Rights Reserved **/ public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { - public ClassPathMapperScanner(BeanDefinitionRegistry registry) { + + private Class clazz; + public ClassPathMapperScanner(BeanDefinitionRegistry registry, Class cls) { super(registry, false); + this.clazz = cls; } public void registerFilters() { @@ -65,6 +68,7 @@ public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner { // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); + definition.getConstructorArgumentValues().addGenericArgumentValue(clazz.getName()); definition.setBeanClass(MapperFactoryBean.class); logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); diff --git a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/EsMapperScan.java b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/EsMapperScan.java index 86b4971a1730da25fa2c851b636924dcb4c54f63..a0ddb4bc3305432e6a7bb5376cb102b44e96d1a1 100644 --- a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/EsMapperScan.java +++ b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/EsMapperScan.java @@ -1,6 +1,7 @@ package org.dromara.easyes.starter.register; +import org.dromara.easyes.core.kernel.BaseEsMapperImpl; import org.springframework.context.annotation.Import; import java.lang.annotation.*; @@ -15,5 +16,19 @@ import java.lang.annotation.*; @Target(ElementType.TYPE) @Import(MapperScannerRegister.class) public @interface EsMapperScan { - String value(); + String[] value(); + String[] basePackages() default {}; + + Class[] basePackageClasses() default {}; + + Class baseMapperClass() default BaseEsMapperImpl.class; + + /** + * Returns the postfix to be used when looking up custom repository implementations. Defaults to {@literal Impl}. So + * for a repository named {@code PersonRepository} the corresponding implementation class will be looked up scanning + * for {@code PersonRepositoryImpl}. + * need CustomRepositoryImplementationDetector to support custom implementations Repository + * @return {@literal Impl} by default. + */ + String repositoryImplementationPostfix() default "Impl"; } diff --git a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperFactoryBean.java b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperFactoryBean.java index e9cf660725f06f757090229f83799c67544032a9..03ce74b25f1b1bfdb85c0be50db9d6a4a3f3cd84 100644 --- a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperFactoryBean.java +++ b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperFactoryBean.java @@ -1,8 +1,10 @@ package org.dromara.easyes.starter.register; +import org.apache.lucene.util.IOUtils; import org.dromara.easyes.annotation.EsDS; import org.dromara.easyes.annotation.Intercepts; import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum; +import org.dromara.easyes.common.exception.EasyEsException; import org.dromara.easyes.common.utils.LogUtils; import org.dromara.easyes.common.utils.RestHighLevelClientUtils; import org.dromara.easyes.common.utils.TypeUtils; @@ -10,6 +12,7 @@ import org.dromara.easyes.core.biz.EntityInfo; import org.dromara.easyes.core.cache.BaseCache; import org.dromara.easyes.core.cache.GlobalConfigCache; import org.dromara.easyes.core.config.GlobalConfig; +import org.dromara.easyes.core.kernel.BaseEsMapper; import org.dromara.easyes.core.proxy.EsMapperProxy; import org.dromara.easyes.core.toolkit.EntityInfoHelper; import org.dromara.easyes.extension.context.Interceptor; @@ -19,14 +22,21 @@ import org.dromara.easyes.starter.config.EasyEsConfigProperties; import org.dromara.easyes.starter.factory.IndexStrategyFactory; import org.dromara.easyes.starter.service.AutoProcessIndexService; import org.elasticsearch.client.RestHighLevelClient; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import java.beans.Introspector; +import java.lang.reflect.Constructor; import java.lang.reflect.Proxy; +import java.util.Arrays; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; import static org.dromara.easyes.common.constants.BaseEsConstants.ZERO; import static org.dromara.easyes.common.utils.RestHighLevelClientUtils.DEFAULT_DS; @@ -36,9 +46,9 @@ import static org.dromara.easyes.common.utils.RestHighLevelClientUtils.DEFAULT_D *

* Copyright © 2021 xpc1024 All Rights Reserved **/ -public class MapperFactoryBean implements FactoryBean { +public class MapperFactoryBean implements FactoryBean { private Class mapperInterface; - + private Class repositoryBaseClass; @Autowired private RestHighLevelClientUtils restHighLevelClientUtils; @@ -58,12 +68,13 @@ public class MapperFactoryBean implements FactoryBean { public MapperFactoryBean(Class mapperInterface) { this.mapperInterface = mapperInterface; } + public MapperFactoryBean(Class mapperInterface, Class repositoryBaseClass) { + this.mapperInterface = mapperInterface; + this.repositoryBaseClass = repositoryBaseClass; + } @Override public T getObject() throws Exception { - - EsMapperProxy esMapperProxy = new EsMapperProxy<>(mapperInterface, new ConcurrentHashMap<>()); - // 获取实体类 Class entityClass = TypeUtils.getInterfaceT(mapperInterface, ZERO); @@ -73,8 +84,15 @@ public class MapperFactoryBean implements FactoryBean { //获取动态数据源 若未配置多数据源,则使用默认数据源 String restHighLevelClientId = Optional.ofNullable(mapperInterface.getAnnotation(EsDS.class)).map(EsDS::value).orElse(DEFAULT_DS); RestHighLevelClient client = restHighLevelClientUtils.getClient(restHighLevelClientId); - BaseCache.initCache(mapperInterface, entityClass, client); + // 优先加载自定义实现类 + //BaseEsMapper esMapper = getCustomizedMapperImpl(); + + BaseEsMapper esMapper = getTargetRepositoryViaRef(client, entityClass); + + BaseCache.initCache(mapperInterface, entityClass, esMapper); + + EsMapperProxy esMapperProxy = new EsMapperProxy<>(mapperInterface, new ConcurrentHashMap<>()); // 创建代理 T t = (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy); @@ -98,6 +116,29 @@ public class MapperFactoryBean implements FactoryBean { return interceptorChain.pluginAll(t); } + private R getTargetRepositoryViaRef(Object... constructorArguments) { + return instantiateClass(repositoryBaseClass, constructorArguments); + } + + private R getCustomizedMapperImpl() { + boolean detector = mapperInterface.getDeclaredMethods().length > 0 || + applicationContext.getBean(getImplementationName()) != null; + if (detector) { + Map map = (Map) applicationContext.getBean(getImplementationName()); + if (map.size() > 1) { + throw new EasyEsException("more than one mapper found"); + } + if (map.size() == 1) { + return (R) map.values().iterator().next(); + } + } + return null; + } + + private String getImplementationName() { + return Introspector.decapitalize(mapperInterface.getSimpleName()) + "Impl"; + } + @Override public Class getObjectType() { return this.mapperInterface; @@ -126,4 +167,27 @@ public class MapperFactoryBean implements FactoryBean { return interceptorChainHolder.getInterceptorChain(); } + protected final R instantiateClass(Class baseClass, Object... constructorArguments) { + Class[] constructorArgumentsType = null; + if (constructorArguments.length > 0) { + constructorArgumentsType = new Class[constructorArguments.length]; + for (int i = 0; i < constructorArguments.length; i++) { + constructorArgumentsType[i] = constructorArguments[i].getClass(); + } + } + + //ClassUtils.getConstructorIfAvailable(baseClass, constructorArguments); + Optional> constructor; + try { + constructor = Optional.ofNullable(ReflectionUtils.accessibleConstructor(baseClass, constructorArgumentsType)); + } catch (NoSuchMethodException e) { + throw new EasyEsException(e); + } + return constructor.map(it -> (R) BeanUtils.instantiateClass(it, constructorArguments)) + .orElseThrow(() -> new IllegalStateException(String.format( + "No suitable constructor found on %s to match the given arguments: %s. Make sure you implement a constructor taking these", + baseClass, Arrays.stream(constructorArguments).map(Object::getClass).map(ClassUtils::getQualifiedName) + .collect(Collectors.joining(", "))))); + } + } diff --git a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperScannerRegister.java b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperScannerRegister.java index 088d4c2e48d3ec53d6e5845ef40f99363f634e38..39d77f80e4b7c5bf4fecd507d3636dbbf069ac8a 100644 --- a/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperScannerRegister.java +++ b/easy-es-boot-starter/src/main/java/org/dromara/easyes/starter/register/MapperScannerRegister.java @@ -2,6 +2,9 @@ package org.dromara.easyes.starter.register; import org.dromara.easyes.common.utils.EEVersionUtils; import org.dromara.easyes.common.utils.LogUtils; +import org.dromara.easyes.core.kernel.BaseEsMapper; +import org.elasticsearch.common.util.set.Sets; +import org.omg.CORBA.portable.Streamable; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.EnvironmentAware; import org.springframework.context.ResourceLoaderAware; @@ -10,12 +13,18 @@ import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.env.Environment; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.ClassUtils; +import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; +import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import static org.dromara.easyes.common.constants.BaseEsConstants.*; @@ -28,12 +37,51 @@ import static org.dromara.easyes.common.constants.BaseEsConstants.*; public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { private ResourceLoader resourceLoader; private Environment environment; + private AnnotationMetadata configMetadata; + private AnnotationAttributes attributes; + + private static final String BASE_PACKAGES = "basePackages"; + private static final String BASE_PACKAGE_CLASSES = "basePackageClasses"; + private static final String BASE_MAPPER_CLASS = "baseMapperClass"; + public Set getBasePackages() { + String[] value = attributes.getStringArray("value"); + String[] basePackages = attributes.getStringArray(BASE_PACKAGES); + Class[] basePackageClasses = attributes.getClassArray(BASE_PACKAGE_CLASSES); + + // Default configuration - return package of annotated class + if (value.length == 0 && basePackages.length == 0 && basePackageClasses.length == 0) { + String className = configMetadata.getClassName(); + return Sets.newHashSet(ClassUtils.getPackageName(className)); + } + + Set packages = new HashSet<>(); + packages.addAll(Arrays.asList(value)); + packages.addAll(Arrays.asList(basePackages)); + + Arrays.stream(basePackageClasses)// + .map(ClassUtils::getPackageName)// + .forEach(it -> packages.add(it)); + + return packages; + } + + public Class getBaseMapperClass() { + return attributes.getClass(BASE_MAPPER_CLASS); + } + @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + public Class getAnnotation() { + return EsMapperScan.class; + } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { Boolean enable = Optional.ofNullable(environment.getProperty(ENABLE_PREFIX)).map(Boolean::parseBoolean).orElse(Boolean.TRUE); @@ -108,15 +156,21 @@ public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, Res System.out.println("----------------------------------------------------------->"); } - AnnotationAttributes mapperScanAttrs = AnnotationAttributes - .fromMap(importingClassMetadata.getAnnotationAttributes(EsMapperScan.class.getName())); - if (mapperScanAttrs != null) { - registerBeanDefinitions(mapperScanAttrs, registry); + configMetadata = importingClassMetadata; + Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(getAnnotation().getName()); + + if (annotationAttributes == null) { + throw new IllegalStateException(String.format("Unable to obtain annotation attributes for %s", getAnnotation())); + } + + attributes = AnnotationAttributes.fromMap(annotationAttributes); + if (attributes != null) { + registerBeanDefinitions(attributes, registry); } } void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) { - ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); + ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry, getBaseMapperClass()); // this check is needed in Spring 3.1 Optional.ofNullable(resourceLoader).ifPresent(scanner::setResourceLoader); List basePackages = new ArrayList<>(); @@ -126,12 +180,8 @@ public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, Res .collect(Collectors.toList())); scanner.registerFilters(); - scanner.doScan(StringUtils.toStringArray(basePackages)); - } - - @Override - public void setEnvironment(Environment environment) { - this.environment = environment; +// scanner.doScan(StringUtils.toStringArray(basePackages)); + scanner.doScan(StringUtils.toStringArray(getBasePackages())); } } diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/biz/EntityInfo.java b/easy-es-core/src/main/java/org/dromara/easyes/core/biz/EntityInfo.java index 6536320b1d8eafd1429c1374ff69141911ccf8a4..984f593949e92ca793fb878f96fcf3a55b92a0a9 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/biz/EntityInfo.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/biz/EntityInfo.java @@ -47,6 +47,12 @@ public class EntityInfo { * 配置的路由 */ private String routing; + private Field routingField; + /** + * 配置的索引分片 + */ + private String indexShardPattern; + private Field groupField; /** * 新索引名(由EE在更新索引时自动创建) */ diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/cache/BaseCache.java b/easy-es-core/src/main/java/org/dromara/easyes/core/cache/BaseCache.java index 0fea52d4e21c185a4b810fd0e362ad1e9cc66ead..aa958ba811f59dbc27148a9fb42063909bd7c390 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/cache/BaseCache.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/cache/BaseCache.java @@ -1,9 +1,12 @@ package org.dromara.easyes.core.cache; +import lombok.Getter; +import lombok.Setter; import org.dromara.easyes.common.constants.BaseEsConstants; import org.dromara.easyes.common.utils.CollectionUtils; import org.dromara.easyes.common.utils.ExceptionUtils; import org.dromara.easyes.core.biz.EntityInfo; +import org.dromara.easyes.core.kernel.BaseEsMapper; import org.dromara.easyes.core.kernel.BaseEsMapperImpl; import org.dromara.easyes.core.toolkit.EntityInfoHelper; import org.dromara.easyes.core.toolkit.FieldUtils; @@ -19,15 +22,20 @@ import java.util.concurrent.ConcurrentHashMap; * Copyright © 2021 xpc1024 All Rights Reserved **/ public class BaseCache { + + @Getter + @Setter + private Class mapperBaseClass; /** * 用于存放BaseEsMapper的所有实例 */ - private static final Map, BaseEsMapperImpl> baseEsMapperInstanceMap = new ConcurrentHashMap<>(); + private static final Map, BaseEsMapper> baseEsMapperInstanceMap = new ConcurrentHashMap<>(); /** * 用于存放Es entity 中的字段的get/is方法 */ private static final Map, Map> baseEsEntityMethodMap = new ConcurrentHashMap<>(); + /** * 初始化缓存 * @@ -35,13 +43,12 @@ public class BaseCache { * @param client es客户端 * @param entityClass 实体类 */ - public static void initCache(Class mapperInterface, Class entityClass, RestHighLevelClient client) { + public static void initCache(Class mapperInterface, Class entityClass, BaseEsMapper mapper) { // 初始化baseEsMapper的所有实现类实例 - BaseEsMapperImpl baseEsMapper = new BaseEsMapperImpl(); - baseEsMapper.setClient(client); - - baseEsMapper.setEntityClass(entityClass); - baseEsMapperInstanceMap.put(mapperInterface, baseEsMapper); +// BaseEsMapper baseEsMapper = new BaseEsMapperImpl(client, entityClass); +// baseEsMapper.setClient(client); +// baseEsMapper.setEntityClass(entityClass); + baseEsMapperInstanceMap.put(mapperInterface, mapper); // 初始化entity中所有字段(注解策略生效) Map invokeMethodsMap = initInvokeMethodsMap(entityClass); @@ -89,7 +96,7 @@ public class BaseCache { * @param mapperInterface mapper接口 * @return 实现类 */ - public static BaseEsMapperImpl getBaseEsMapperInstance(Class mapperInterface) { + public static BaseEsMapper getBaseEsMapperInstance(Class mapperInterface) { return Optional.ofNullable(baseEsMapperInstanceMap.get(mapperInterface)) .orElseThrow(() -> ExceptionUtils.eee("no such instance", mapperInterface)); } diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/kernel/BaseEsMapperImpl.java b/easy-es-core/src/main/java/org/dromara/easyes/core/kernel/BaseEsMapperImpl.java index 7f98d65b8b899cdf9effe03ea63583b7c169d1ef..1a90d233d7e6088a4b9f28ffd168d9f8aa3ce909 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/kernel/BaseEsMapperImpl.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/kernel/BaseEsMapperImpl.java @@ -5,6 +5,9 @@ import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializeFilter; import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SimplePropertyPreFilter; +import lombok.AllArgsConstructor; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.Setter; import lombok.SneakyThrows; import org.apache.http.util.EntityUtils; @@ -16,6 +19,7 @@ import org.dromara.easyes.common.constants.BaseEsConstants; import org.dromara.easyes.common.enums.EsQueryTypeEnum; import org.dromara.easyes.common.enums.MethodEnum; import org.dromara.easyes.common.enums.OrderTypeEnum; +import org.dromara.easyes.common.exception.EasyEsException; import org.dromara.easyes.common.utils.*; import org.dromara.easyes.core.biz.*; import org.dromara.easyes.core.cache.BaseCache; @@ -61,11 +65,23 @@ import org.elasticsearch.search.SearchHits; import org.elasticsearch.search.aggregations.metrics.ParsedCardinality; import org.elasticsearch.search.builder.SearchSourceBuilder; import org.elasticsearch.search.fetch.subphase.highlight.HighlightField; +import org.springframework.core.annotation.AnnotatedElementUtils; +import java.beans.Introspector; import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.util.*; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; @@ -86,14 +102,29 @@ public class BaseEsMapperImpl implements BaseEsMapper { /** * restHighLevel client */ - @Setter + @NonNull private RestHighLevelClient client; /** * T 对应的类 */ - @Setter +// @Setter + @NonNull private Class entityClass; + public BaseEsMapperImpl(RestHighLevelClient client) { + this.client = client; + Type type = getClass().getGenericSuperclass(); + if (type instanceof ParameterizedType) { + ParameterizedType paramType = (ParameterizedType) type; + this.entityClass = (Class) paramType.getActualTypeArguments()[0]; + } + } + + public BaseEsMapperImpl(RestHighLevelClient client, Class entityType) { + this.client = client; + this.entityClass = entityType; + } + @Override public Class getEntityClass() { return entityClass; @@ -329,24 +360,80 @@ public class BaseEsMapperImpl implements BaseEsMapper { } } + private String getIndexName(EntityInfo entityInfo, T entity) { + String baseIndexName = entityInfo.getIndexName(); + String indexShardSlice; + try { + indexShardSlice = getIndexShardSlice(entityInfo, entity); + } catch (ParseException e) { + throw new EasyEsException(e); + } + return StringUtils.isBlank(indexShardSlice) ? baseIndexName : + baseIndexName.concat("-").concat(indexShardSlice); + } + + private String getIndexShardSlice(EntityInfo entityInfo, T entity) throws ParseException { + String suffix = null; + if (Objects.nonNull(entityInfo.getGroupField())) { + String slicePattern = entityInfo.getIndexShardPattern(); + DateFormat format = new SimpleDateFormat(slicePattern); + Object groupValue = getFieldValue(entity, entityInfo.getGroupField()); + if (Objects.nonNull(groupValue)) { + if (groupValue instanceof Date) { + suffix = format.format(groupValue); + } else if (groupValue instanceof LocalDate) { + // 转换为java.util.Date + Date date = Date.from(((LocalDate) groupValue).atStartOfDay(ZoneId.systemDefault()).toInstant()); + suffix = format.format(date); + } else if (groupValue instanceof String) { + suffix = format.format(format.parse(groupValue.toString())); + } + } + } + return suffix; + } + + private E getFieldValue(T entity, Field field) { + try { + if (field.getModifiers() != Modifier.PUBLIC) { + field.setAccessible(true); + } + return (E) field.get(entity); + } catch (IllegalAccessException e) { + throw new EasyEsException(e); + } + } + @Override public Integer insert(T entity) { - return insert(null, null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass); + // TODO: 2024/8/1 也可以根据@Routing注解支持 + String routing = entityInfo.getRouting(); + if (Objects.isNull(routing)) { + if (Objects.nonNull(entityInfo.getRoutingField())) { +// routing = getFieldValue(entity, entityInfo.getRoutingField()); +// entityInfo.setRouting(routing); + entityInfo.setRouting(Introspector.decapitalize(entityInfo.getRoutingField().getName())); + } + } + return insert(entityInfo.getRouting(), entity); } @Override public Integer insert(String routing, T entity) { - return insert(routing, null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + return insert(routing, null, entity); } @Override public Integer insert(String routing, String parentId, T entity) { - return insert(routing, parentId, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass); + String indexName = getIndexName(entityInfo, entity); + return insert(routing, parentId, entity, indexName); } @Override public Integer insert(T entity, String... indexNames) { - return insert(null, null, entity, indexNames); + return insert(null, entity, indexNames); } @Override @@ -366,22 +453,26 @@ public class BaseEsMapperImpl implements BaseEsMapper { @Override public Integer insertBatch(Collection entityList) { - return insertBatch(null, null, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + return insertBatch(null, entityList); } @Override public Integer insertBatch(String routing, Collection entityList) { - return insertBatch(routing, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + return insertBatch(routing, null, entityList); } @Override public Integer insertBatch(String routing, String parentId, Collection entityList) { - return insertBatch(routing, parentId, entityList, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + String[] indexNames = (String[]) entityList.stream() + .map(entity -> getIndexName(EntityInfoHelper.getEntityInfo(entityClass), entity)) + .distinct() + .toArray(); + return insertBatch(routing, parentId, entityList, indexNames); } @Override public Integer insertBatch(Collection entityList, String... indexNames) { - return insertBatch(null, null, entityList, indexNames); + return insertBatch(null, entityList, indexNames); } @Override @@ -472,12 +563,14 @@ public class BaseEsMapperImpl implements BaseEsMapper { @Override public Integer updateById(T entity) { - return updateById(null, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + String index = getIndexName(EntityInfoHelper.getEntityInfo(entityClass), entity); + return updateById(null, entity, index); } @Override public Integer updateById(String routing, T entity) { - return updateById(routing, entity, EntityInfoHelper.getEntityInfo(entityClass).getIndexName()); + String index = getIndexName(EntityInfoHelper.getEntityInfo(entityClass), entity); + return updateById(routing, entity, index); } @Override diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/proxy/EsMapperProxy.java b/easy-es-core/src/main/java/org/dromara/easyes/core/proxy/EsMapperProxy.java index 2f3335f44b5c9792ed3940863ef37f6b73432255..a0569de527ba8bcf6102caebb3a6f67b5456c29c 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/proxy/EsMapperProxy.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/proxy/EsMapperProxy.java @@ -1,7 +1,7 @@ package org.dromara.easyes.core.proxy; import org.dromara.easyes.core.cache.BaseCache; -import org.dromara.easyes.core.kernel.BaseEsMapperImpl; +import org.dromara.easyes.core.kernel.BaseEsMapper; import java.io.Serializable; import java.lang.invoke.MethodHandle; @@ -66,7 +66,7 @@ public class EsMapperProxy implements InvocationHandler, Serializable { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - BaseEsMapperImpl baseEsMapperInstance = BaseCache.getBaseEsMapperInstance(mapperInterface); + BaseEsMapper baseEsMapperInstance = BaseCache.getBaseEsMapperInstance(mapperInterface); if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(baseEsMapperInstance, args); } @@ -75,7 +75,7 @@ public class EsMapperProxy implements InvocationHandler, Serializable { return cachedInvoker(method, baseEsMapperInstance).invoke(proxy, method, args); } - private EsMapperMethodInvoker cachedInvoker(Method method, BaseEsMapperImpl baseEsMapperInstance) + private EsMapperMethodInvoker cachedInvoker(Method method, BaseEsMapper baseEsMapperInstance) throws Throwable { try { return methodCache.computeIfAbsent(method, m -> { @@ -119,9 +119,9 @@ public class EsMapperProxy implements InvocationHandler, Serializable { private static class PlainMethodInvoker implements EsMapperMethodInvoker { - private final BaseEsMapperImpl baseEsMapperInstance; + private final BaseEsMapper baseEsMapperInstance; - public PlainMethodInvoker(BaseEsMapperImpl baseEsMapperInstance) { + public PlainMethodInvoker(BaseEsMapper baseEsMapperInstance) { this.baseEsMapperInstance = baseEsMapperInstance; } diff --git a/easy-es-core/src/main/java/org/dromara/easyes/core/toolkit/EntityInfoHelper.java b/easy-es-core/src/main/java/org/dromara/easyes/core/toolkit/EntityInfoHelper.java index c9b7ca4810163f2dd2f325a9e5be3662458bbb5b..e59f827de07bd39b22d35ec6f3f8e1aba3001b5c 100644 --- a/easy-es-core/src/main/java/org/dromara/easyes/core/toolkit/EntityInfoHelper.java +++ b/easy-es-core/src/main/java/org/dromara/easyes/core/toolkit/EntityInfoHelper.java @@ -29,6 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import static org.dromara.easyes.common.constants.BaseEsConstants.*; +import static org.springframework.util.Assert.hasText; /** * 实体字段信息工具类 @@ -413,6 +414,33 @@ public class EntityInfoHelper { entityInfo.getDistanceDecimalPlaces().add(distance.decimalPlaces()); hasAnnotation = true; } + + // 初始化封装GroupField注解信息 + if (field.isAnnotationPresent(GroupField.class)) { + GroupField group = field.getAnnotation(GroupField.class); + if (group != null) { + entityInfo.setIndexShardPattern(group.dateFormat()); + field.setAccessible(true); + entityInfo.setGroupField(field); + } + hasAnnotation = true; + } + // 初始化封装GroupField注解信息 + Routing routingAnnotation = null; + if (field.isAnnotationPresent(Routing.class)) { + routingAnnotation = field.getAnnotation(Routing.class); + if (routingAnnotation != null) { + hasText(routingAnnotation.value(), "@Routing annotation must contain a non-empty value"); + field.setAccessible(true); + entityInfo.setRoutingField(field); + } + hasAnnotation = true; + } else { + routingAnnotation = clazz.getAnnotation(Routing.class); + if (routingAnnotation != null) { + entityInfo.setRouting(routingAnnotation.value()); + } + } return hasAnnotation; } diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Document.java b/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Document.java index 6e3d2327e348a23cdc539a10409234c7c72f8db0..c312822c685683c6a763ba6f595255302b77a5fc 100644 --- a/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Document.java +++ b/easy-es-test/src/main/java/org/dromara/easyes/test/entity/Document.java @@ -26,7 +26,7 @@ import java.util.List; @Accessors(chain = true) @Settings(shardsNum = 3, replicasNum = 2, settingsProvider = MySettingsProvider.class) @IndexName(value = "easyes_document", keepGlobalPrefix = true, refreshPolicy = RefreshPolicy.IMMEDIATE) -@Join(nodes = {@Node(parentClass = Document.class, childClasses = {Author.class, Comment.class}), @Node(parentClass = Author.class, childClasses = Contact.class)}) +//@Join(nodes = {@Node(parentClass = Document.class, childClasses = {Author.class, Comment.class}), @Node(parentClass = Author.class, childClasses = Contact.class)}) public class Document { /** * es中的唯一id,字段名随便起,我这里演示用esId,你也可以用id(推荐),bizId等. @@ -37,6 +37,7 @@ public class Document { /** * 文档标题,不指定类型默认被创建为keyword类型,可进行精确查询 */ + @Routing(value = "title") private String title; /** * 副标题 @@ -66,6 +67,7 @@ public class Document { /** * 创建时间 */ + @GroupField @IndexField(fieldType = FieldType.DATE, dateFormat = "yyyy-MM-dd HH:mm:ss") private String gmtCreate; /** diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/DocumentMapper.java b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/DocumentMapper.java index 59554b0928edb8ff3f9a11daa8c94f75061b4723..4fa3a109e465b1f2311efcb6feb8327f88aa2fae 100644 --- a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/DocumentMapper.java +++ b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/DocumentMapper.java @@ -24,4 +24,6 @@ public interface DocumentMapper extends BaseEsMapper { return selectList(wrapper); } + List testCustomerMethod(String id); + } diff --git a/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/impl/DocumentMapperImpl.java b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/impl/DocumentMapperImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..7ba984c1ee275a2531976eaa8af9cc2cc1230d42 --- /dev/null +++ b/easy-es-test/src/main/java/org/dromara/easyes/test/mapper/impl/DocumentMapperImpl.java @@ -0,0 +1,27 @@ +package org.dromara.easyes.test.mapper.impl; + +import org.dromara.easyes.core.kernel.BaseEsMapperImpl; +import org.dromara.easyes.test.entity.Document; +import org.dromara.easyes.test.mapper.DocumentMapper; +import org.elasticsearch.client.RestHighLevelClient; +import org.springframework.stereotype.Component; +import org.springframework.test.context.jdbc.Sql; + +import java.util.List; + +/** + * + */ +@Component +public class DocumentMapperImpl extends BaseEsMapperImpl implements DocumentMapper { + + public DocumentMapperImpl(RestHighLevelClient client) { + super(client); + } + + @Sql("SELECT * FROM easyes_document where esId = ?") + @Override + public List testCustomerMethod(String id) { + return null; + } +}