mirror of
https://gitee.com/dromara/easy-es.git
synced 2025-12-06 17:18:57 +08:00
easy-es-spring 关于spring的适配,多数据源待完成。。
This commit is contained in:
parent
d9eedb1b02
commit
21c1bd90c2
@ -2,7 +2,7 @@ package org.dromara.easyes.starter.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.easyes.common.enums.SchemaEnum;
|
||||
import org.dromara.easyes.core.config.GlobalConfig;
|
||||
import org.dromara.easyes.common.property.GlobalConfig;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ -9,7 +9,7 @@ import org.dromara.easyes.common.utils.TypeUtils;
|
||||
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.common.property.GlobalConfig;
|
||||
import org.dromara.easyes.core.proxy.EsMapperProxy;
|
||||
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
|
||||
import org.dromara.easyes.extension.context.Interceptor;
|
||||
|
||||
@ -18,6 +18,10 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.easy-es</groupId>
|
||||
<artifactId>easy-es-annotation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.elasticsearch.client</groupId>
|
||||
<artifactId>elasticsearch-rest-high-level-client</artifactId>
|
||||
|
||||
@ -18,6 +18,10 @@ public interface BaseEsConstants {
|
||||
* 是否开启iKun模式
|
||||
*/
|
||||
String ENABLE_I_KUN_MODE = "easy-es.global-config.i-kun-mode";
|
||||
/**
|
||||
* 是否开启iKun模式
|
||||
*/
|
||||
String INDEX_MODE = "easy-es.global-config.process-index-mode";
|
||||
/**
|
||||
* 默认主键名称
|
||||
*/
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
package org.dromara.easyes.core.config;
|
||||
|
||||
package org.dromara.easyes.common.property;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.easyes.annotation.rely.FieldStrategy;
|
||||
@ -8,7 +8,7 @@ import lombok.Setter;
|
||||
import org.dromara.easyes.annotation.IndexField;
|
||||
import org.dromara.easyes.annotation.rely.FieldStrategy;
|
||||
import org.dromara.easyes.annotation.rely.FieldType;
|
||||
import org.dromara.easyes.core.config.GlobalConfig;
|
||||
import org.dromara.easyes.common.property.GlobalConfig;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
package org.dromara.easyes.core.cache;
|
||||
|
||||
import org.dromara.easyes.core.config.GlobalConfig;
|
||||
import org.dromara.easyes.common.property.GlobalConfig;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@ -20,7 +20,7 @@ import org.dromara.easyes.common.utils.*;
|
||||
import org.dromara.easyes.core.biz.*;
|
||||
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.common.property.GlobalConfig;
|
||||
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
|
||||
import org.dromara.easyes.core.toolkit.FieldUtils;
|
||||
import org.dromara.easyes.core.toolkit.IndexUtils;
|
||||
|
||||
@ -0,0 +1,25 @@
|
||||
package org.dromara.easyes.core.service;
|
||||
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
|
||||
/**
|
||||
* 自动托管索引接口
|
||||
* <p>
|
||||
* Copyright © 2022 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public interface AutoProcessIndexService {
|
||||
/**
|
||||
* 获取当前策略类型
|
||||
*
|
||||
* @return 策略类型
|
||||
*/
|
||||
Integer getStrategyType();
|
||||
|
||||
/**
|
||||
* 异步处理索引
|
||||
*
|
||||
* @param entityClass 实体类
|
||||
* @param client restHighLevelClient
|
||||
*/
|
||||
void processIndexAsync(Class<?> entityClass, RestHighLevelClient client);
|
||||
}
|
||||
@ -14,7 +14,7 @@ import org.dromara.easyes.core.biz.EntityInfo;
|
||||
import org.dromara.easyes.core.biz.HighLightParam;
|
||||
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.common.property.GlobalConfig;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
@ -5,7 +5,7 @@ import org.dromara.easyes.common.constants.BaseEsConstants;
|
||||
import org.dromara.easyes.common.params.SFunction;
|
||||
import org.dromara.easyes.common.utils.StringUtils;
|
||||
import org.dromara.easyes.core.cache.GlobalConfigCache;
|
||||
import org.dromara.easyes.core.config.GlobalConfig;
|
||||
import org.dromara.easyes.common.property.GlobalConfig;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
|
||||
@ -12,7 +12,7 @@ import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum;
|
||||
import org.dromara.easyes.common.utils.*;
|
||||
import org.dromara.easyes.core.biz.*;
|
||||
import org.dromara.easyes.core.cache.GlobalConfigCache;
|
||||
import org.dromara.easyes.core.config.GlobalConfig;
|
||||
import org.dromara.easyes.common.property.GlobalConfig;
|
||||
import org.elasticsearch.action.admin.indices.alias.Alias;
|
||||
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
|
||||
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
|
||||
|
||||
@ -19,10 +19,6 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.easy-es</groupId>
|
||||
<artifactId>easy-es-annotation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.easy-es</groupId>
|
||||
<artifactId>easy-es-common</artifactId>
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
<module>../easy-es-annotation</module>
|
||||
<module>../easy-es-common</module>
|
||||
<module>../easy-es-solon-plugin</module>
|
||||
<module>../easy-es-spring</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
@ -36,6 +37,7 @@
|
||||
<fastjson.version>1.2.83</fastjson.version>
|
||||
<codec.version>1.13</codec.version>
|
||||
<spring-boot.version>2.6.10</spring-boot.version>
|
||||
<spring.version>5.3.22</spring.version>
|
||||
<solon.version>3.0.5</solon.version>
|
||||
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
|
||||
</properties>
|
||||
@ -105,10 +107,27 @@
|
||||
<artifactId>solon</artifactId>
|
||||
<version>${solon.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.noear</groupId>
|
||||
<artifactId>solon-serialization-fastjson</artifactId>
|
||||
<version>${solon.version}</version>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@ -2,7 +2,7 @@ package org.dromara.easyes.solon.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.easyes.common.enums.SchemaEnum;
|
||||
import org.dromara.easyes.core.config.GlobalConfig;
|
||||
import org.dromara.easyes.common.property.GlobalConfig;
|
||||
import org.noear.solon.annotation.Condition;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
|
||||
@ -10,7 +10,7 @@ import org.dromara.easyes.common.utils.TypeUtils;
|
||||
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.common.property.GlobalConfig;
|
||||
import org.dromara.easyes.core.kernel.BaseEsMapper;
|
||||
import org.dromara.easyes.core.proxy.EsMapperProxy;
|
||||
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
|
||||
@ -25,16 +25,12 @@ import org.noear.solon.Solon;
|
||||
import org.noear.solon.core.AppContext;
|
||||
import org.noear.solon.core.BeanBuilder;
|
||||
import org.noear.solon.core.BeanWrap;
|
||||
import org.noear.solon.core.route.PathRule;
|
||||
import org.noear.solon.core.util.ClassUtil;
|
||||
import org.noear.solon.core.util.LogUtil;
|
||||
import org.noear.solon.core.util.ScanUtil;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.dromara.easyes.common.constants.BaseEsConstants.*;
|
||||
|
||||
50
easy-es-spring/pom.xml
Normal file
50
easy-es-spring/pom.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.easy-es</groupId>
|
||||
<artifactId>easy-es-parent</artifactId>
|
||||
<version>2.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>easy-es-spring</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.easy-es</groupId>
|
||||
<artifactId>easy-es-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-beans</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.4.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${spring-boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,125 @@
|
||||
package org.dromara.easyes.spring;
|
||||
|
||||
import lombok.Setter;
|
||||
import org.dromara.easyes.common.utils.RestHighLevelClientUtils;
|
||||
import org.dromara.easyes.common.property.GlobalConfig;
|
||||
import org.dromara.easyes.core.kernel.BaseEsMapper;
|
||||
import org.dromara.easyes.spring.factory.IndexStrategyFactory;
|
||||
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanDefinitionHolder;
|
||||
import org.springframework.beans.factory.support.AbstractBeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 扫描指定路径下的所有接口
|
||||
* <p>
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
|
||||
|
||||
@Setter
|
||||
private IndexStrategyFactory indexStrategyFactory;
|
||||
|
||||
@Setter
|
||||
private RestHighLevelClientUtils restHighLevelClientUtils;
|
||||
|
||||
@Setter
|
||||
private GlobalConfig globalConfig;
|
||||
|
||||
public ClassPathMapperScanner(BeanDefinitionRegistry registry) {
|
||||
super(registry, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置扫描注册
|
||||
* @param registry bean注册器
|
||||
* @param environment 变量
|
||||
* @author MoJie
|
||||
*/
|
||||
public ClassPathMapperScanner(BeanDefinitionRegistry registry, Environment environment) {
|
||||
super(registry, false, environment);
|
||||
}
|
||||
|
||||
public void registerFilters() {
|
||||
// default include filter that accepts all classes
|
||||
addIncludeFilter((metadataReader, metadataReaderFactory) -> {
|
||||
// 跳过非ee的mapper,比如瞎几把写的接口,没有继承BaseEsMapper
|
||||
String className = metadataReader.getClassMetadata().getClassName();
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
return BaseEsMapper.class.isAssignableFrom(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
logger.debug("mapper not found" + e);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// exclude package-info.java
|
||||
addExcludeFilter((metadataReader, metadataReaderFactory) -> {
|
||||
String className = metadataReader.getClassMetadata().getClassName();
|
||||
return className.endsWith("package-info");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
|
||||
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
|
||||
if (beanDefinitions.isEmpty()) {
|
||||
logger.warn("No Easy-Es mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
|
||||
} else {
|
||||
processBeanDefinitions(beanDefinitions);
|
||||
}
|
||||
return beanDefinitions;
|
||||
}
|
||||
|
||||
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
|
||||
GenericBeanDefinition definition;
|
||||
for (BeanDefinitionHolder holder : beanDefinitions) {
|
||||
definition = (GenericBeanDefinition) holder.getBeanDefinition();
|
||||
String beanClassName = definition.getBeanClassName();
|
||||
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
|
||||
+ "' and '" + beanClassName + "' mapperInterface");
|
||||
|
||||
// the mapper interface is the original class of the bean
|
||||
// but, the actual class of the bean is MapperFactoryBean
|
||||
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
|
||||
definition.setBeanClass(MapperFactoryBean.class);
|
||||
// 属性注入
|
||||
definition.getPropertyValues().add("globalConfig", this.globalConfig);
|
||||
// 索引策略工厂注入
|
||||
definition.getPropertyValues().add("indexStrategyFactory", this.indexStrategyFactory);
|
||||
// easy-es client连接工具类
|
||||
definition.getPropertyValues().add("restHighLevelClientUtils", this.restHighLevelClientUtils);
|
||||
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
|
||||
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
|
||||
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
protected boolean checkCandidate(String beanName, BeanDefinition beanDefinition) {
|
||||
if (super.checkCandidate(beanName, beanDefinition)) {
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("Skipping MapperFactoryBean with name '" + beanName
|
||||
+ "' and '" + beanDefinition.getBeanClassName() + "' mapperInterface"
|
||||
+ ". Bean already defined with the same name!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
package org.dromara.easyes.spring;
|
||||
|
||||
import lombok.Setter;
|
||||
import org.dromara.easyes.annotation.EsDS;
|
||||
import org.dromara.easyes.annotation.Intercepts;
|
||||
import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum;
|
||||
import org.dromara.easyes.common.utils.LogUtils;
|
||||
import org.dromara.easyes.common.utils.RestHighLevelClientUtils;
|
||||
import org.dromara.easyes.common.utils.TypeUtils;
|
||||
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.common.property.GlobalConfig;
|
||||
import org.dromara.easyes.core.proxy.EsMapperProxy;
|
||||
import org.dromara.easyes.core.service.AutoProcessIndexService;
|
||||
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
|
||||
import org.dromara.easyes.extension.context.Interceptor;
|
||||
import org.dromara.easyes.extension.context.InterceptorChain;
|
||||
import org.dromara.easyes.extension.context.InterceptorChainHolder;
|
||||
import org.dromara.easyes.spring.factory.IndexStrategyFactory;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static org.dromara.easyes.common.constants.BaseEsConstants.ZERO;
|
||||
import static org.dromara.easyes.common.utils.RestHighLevelClientUtils.DEFAULT_DS;
|
||||
|
||||
/**
|
||||
* 代理类
|
||||
* <p>
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public class MapperFactoryBean<T> implements FactoryBean<T> {
|
||||
|
||||
private Class<T> mapperInterface;
|
||||
|
||||
@Autowired
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
@Setter
|
||||
private IndexStrategyFactory indexStrategyFactory;
|
||||
|
||||
@Setter
|
||||
private GlobalConfig globalConfig;
|
||||
|
||||
@Setter
|
||||
private RestHighLevelClientUtils restHighLevelClientUtils;
|
||||
|
||||
public MapperFactoryBean() {
|
||||
}
|
||||
|
||||
public MapperFactoryBean(Class<T> mapperInterface) {
|
||||
this.mapperInterface = mapperInterface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getObject() throws Exception {
|
||||
|
||||
EsMapperProxy<T> esMapperProxy = new EsMapperProxy<>(mapperInterface, new ConcurrentHashMap<>());
|
||||
|
||||
// 获取实体类
|
||||
Class<?> entityClass = TypeUtils.getInterfaceT(mapperInterface, ZERO);
|
||||
|
||||
// 初始化缓存
|
||||
GlobalConfigCache.setGlobalConfig(globalConfig);
|
||||
|
||||
//获取动态数据源 若未配置多数据源,则使用默认数据源
|
||||
String restHighLevelClientId = Optional.ofNullable(mapperInterface.getAnnotation(EsDS.class)).map(EsDS::value).orElse(DEFAULT_DS);
|
||||
RestHighLevelClient client = restHighLevelClientUtils.getClient(restHighLevelClientId);
|
||||
BaseCache.initCache(mapperInterface, entityClass, client);
|
||||
|
||||
// 创建代理
|
||||
T t = (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, esMapperProxy);
|
||||
|
||||
// 初始化拦截器链
|
||||
InterceptorChain interceptorChain = this.initInterceptorChain();
|
||||
|
||||
// 异步处理索引创建/更新/数据迁移等
|
||||
if (!ProcessIndexStrategyEnum.MANUAL.equals(globalConfig.getProcessIndexMode())) {
|
||||
// 父子类型,仅针对父类型创建索引,子类型不创建索引
|
||||
EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass);
|
||||
boolean isChild = entityInfo.isChild();
|
||||
if (!isChild) {
|
||||
AutoProcessIndexService autoProcessIndexService = indexStrategyFactory
|
||||
.getByStrategyType(globalConfig.getProcessIndexMode().getStrategyType());
|
||||
autoProcessIndexService.processIndexAsync(entityClass, client);
|
||||
}
|
||||
} else {
|
||||
LogUtils.info("===> manual index mode activated");
|
||||
}
|
||||
return interceptorChain.pluginAll(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getObjectType() {
|
||||
return this.mapperInterface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private InterceptorChain initInterceptorChain() {
|
||||
InterceptorChainHolder interceptorChainHolder = InterceptorChainHolder.getInstance();
|
||||
InterceptorChain interceptorChain = interceptorChainHolder.getInterceptorChain();
|
||||
if (interceptorChain == null) {
|
||||
synchronized (this) {
|
||||
interceptorChainHolder.initInterceptorChain();
|
||||
Map<String, Object> beansWithAnnotation = this.applicationContext.getBeansWithAnnotation(Intercepts.class);
|
||||
beansWithAnnotation.forEach((key, val) -> {
|
||||
if (val instanceof Interceptor) {
|
||||
Interceptor interceptor = (Interceptor) val;
|
||||
interceptorChainHolder.addInterceptor(interceptor);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return interceptorChainHolder.getInterceptorChain();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,315 @@
|
||||
package org.dromara.easyes.spring;
|
||||
|
||||
import lombok.Setter;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.auth.AuthScope;
|
||||
import org.apache.http.auth.UsernamePasswordCredentials;
|
||||
import org.apache.http.client.CredentialsProvider;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.TrustStrategy;
|
||||
import org.apache.http.impl.client.BasicCredentialsProvider;
|
||||
import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
import org.dromara.easyes.common.enums.SchemaEnum;
|
||||
import org.dromara.easyes.common.utils.*;
|
||||
import org.dromara.easyes.spring.factory.IndexStrategyFactory;
|
||||
import org.dromara.easyes.spring.property.EasyEsProperty;
|
||||
import org.elasticsearch.client.RestClient;
|
||||
import org.elasticsearch.client.RestClientBuilder;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.PropertyValue;
|
||||
import org.springframework.beans.PropertyValues;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.config.PropertyResourceConfigurer;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.util.*;
|
||||
|
||||
import static org.dromara.easyes.common.constants.BaseEsConstants.*;
|
||||
|
||||
/**
|
||||
* spring配置类扫描
|
||||
* @author MoJie
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MapperScannerConfigurer
|
||||
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
|
||||
|
||||
@Setter
|
||||
private String basePackage;
|
||||
|
||||
@Setter
|
||||
private IndexStrategyFactory indexStrategyFactory;
|
||||
|
||||
@Setter
|
||||
private RestHighLevelClientUtils restHighLevelClientUtils;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
private String beanName;
|
||||
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
|
||||
Boolean enable = getEnvironment().getProperty(ENABLE_PREFIX, Boolean.TYPE, true);
|
||||
if (!enable) {
|
||||
LogUtils.info("===> Easy-Es is not enabled");
|
||||
return;
|
||||
}
|
||||
this.printBanner();
|
||||
Map<String, PropertyResourceConfigurer> prcs = applicationContext
|
||||
.getBeansOfType(PropertyResourceConfigurer.class, false, false);
|
||||
// 如果spring动态配置上下文中存在,那么到上下文对象中获取/比如在*.properties中配置了键值对
|
||||
if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) {
|
||||
BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext).getBeanFactory()
|
||||
.getBeanDefinition(beanName);
|
||||
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
|
||||
factory.registerBeanDefinition(beanName, mapperScannerBean);
|
||||
for (PropertyResourceConfigurer prc : prcs.values()) {
|
||||
prc.postProcessBeanFactory(factory);
|
||||
}
|
||||
PropertyValues values = mapperScannerBean.getPropertyValues();
|
||||
this.basePackage = getPropertyValue("basePackage", values);
|
||||
}
|
||||
|
||||
// 取变量
|
||||
this.basePackage = Optional.ofNullable(this.basePackage)
|
||||
.map(getEnvironment()::resolvePlaceholders).orElse(null);
|
||||
// 做扫包的操作了、与注解扫包类似,只不过这里是spring配置方式
|
||||
// 在mybatis中配置了很多扫描拦截属性,这里放到后面拓展
|
||||
ClassPathMapperScanner scanner = new ClassPathMapperScanner(beanDefinitionRegistry, getEnvironment());
|
||||
scanner.registerFilters();
|
||||
// 索引策略工厂 -- 需要spring 注册bean xml/component 注解方式均可
|
||||
scanner.setIndexStrategyFactory(this.indexStrategyFactory);
|
||||
EasyEsProperty easyEsProperty = new EasyEsProperty(this.getEnvironment());
|
||||
scanner.setGlobalConfig(easyEsProperty.getGlobalConfig());
|
||||
// easy-es的连接对象注册 需要spring 注册bean xml/component 注解方式均可
|
||||
if (this.restHighLevelClientUtils == null) {
|
||||
RestHighLevelClientUtils restHighLevelClientUtils = new RestHighLevelClientUtils();
|
||||
Map<String, EasyEsProperty> datasourceMap = easyEsProperty.getDatasource();
|
||||
if (CollectionUtils.isEmpty(datasourceMap)) {
|
||||
// 设置默认数据源,兼容不使用多数据源配置场景的老用户使用习惯
|
||||
datasourceMap.put(RestHighLevelClientUtils.DEFAULT_DS, easyEsProperty);
|
||||
}
|
||||
for (String key : datasourceMap.keySet()) {
|
||||
EasyEsProperty easyEsConfigProperties = datasourceMap.get(key);
|
||||
RestHighLevelClientUtils.registerRestHighLevelClient(key, restHighLevelClient(easyEsConfigProperties));
|
||||
}
|
||||
this.restHighLevelClientUtils = restHighLevelClientUtils;
|
||||
}
|
||||
scanner.setRestHighLevelClientUtils(this.restHighLevelClientUtils);
|
||||
scanner.doScan(this.basePackage);
|
||||
}
|
||||
|
||||
private String getPropertyValue(String propertyName, PropertyValues values) {
|
||||
PropertyValue property = values.getPropertyValue(propertyName);
|
||||
if (property == null) {
|
||||
return null;
|
||||
}
|
||||
Object value = property.getValue();
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value instanceof String) {
|
||||
return value.toString();
|
||||
}
|
||||
if (value instanceof TypedStringValue) {
|
||||
return ((TypedStringValue) value).getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Environment getEnvironment() {
|
||||
return this.applicationContext.getEnvironment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* bean 注册完成后校验属性是否注入
|
||||
* @author MoJie
|
||||
*/
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(this.basePackage, "Property 'basePackage' is required. mapper扫包路径为必填。");
|
||||
Assert.notNull(this.indexStrategyFactory, "Property 'indexStrategyFactory' is required. " +
|
||||
"请注册索引策略工厂实现。");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanName(String beanName) {
|
||||
this.beanName = beanName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印banner
|
||||
* @author MoJie
|
||||
*/
|
||||
private void printBanner() {
|
||||
//打印banner @author dazer007
|
||||
Boolean banner = getEnvironment().getProperty(ENABLE_BANNER, Boolean.class, true);
|
||||
if (banner) {
|
||||
Boolean iKunMode = getEnvironment().getProperty(ENABLE_I_KUN_MODE, Boolean.TYPE, false);
|
||||
String versionStr = EEVersionUtils.getJarVersion(this.getClass());
|
||||
String wechatStr = ":: wechat :: 252645816, add and become muscle man! >";
|
||||
if (iKunMode) {
|
||||
System.out.println(" 鸡你太美\n" +
|
||||
" 鸡你实在太美\n" +
|
||||
" 鸡你是太美\n" +
|
||||
" 鸡你太美\n" +
|
||||
" 实在是太美鸡你\n" +
|
||||
" 鸡你 实在是太美鸡你 美\n" +
|
||||
" 鸡你 实在是太美鸡美 太美\n" +
|
||||
" 鸡你 实在是太美鸡美 太美\n" +
|
||||
" 鸡你 实在是太美鸡美 太美\n" +
|
||||
" 鸡你 鸡你实在是美太美 美蓝球球球\n" +
|
||||
"鸡 鸡 鸡你实在是太美 篮球篮球球球球\n" +
|
||||
" 鸡 鸡你太美裆鸡太啊 球球蓝篮球球\n" +
|
||||
" 鸡你太美裆裆鸡美 球球球\n" +
|
||||
" 鸡你裆小 j 鸡太美\n" +
|
||||
" 鸡太美 鸡太美\n" +
|
||||
" 鸡美 鸡美\n" +
|
||||
" 鸡美 鸡美\n" +
|
||||
" 鸡美 鸡美\n" +
|
||||
" 鸡太 鸡太\n" +
|
||||
" 鸡 脚 鸡脚\n" +
|
||||
" 皮 鞋 皮鞋金猴\n" +
|
||||
" 金光 金光 大道\n" +
|
||||
" 大道\n" +
|
||||
" 鸡神保佑 永不宕机 永无BUG");
|
||||
wechatStr = ":: wechat :: 252645816, add and join ikun(小黑子) group! >";
|
||||
} else {
|
||||
System.out.println("\n" +
|
||||
"___ _ _ ___\n" +
|
||||
" | __| __ _ ___ | || | ___ | __| ___\n" +
|
||||
" | _| / _` | (_-< \\_, | |___| | _| (_-<\n" +
|
||||
" |___| \\__,_| /__/_ _|__/ _____ |___| /__/_\n" +
|
||||
"_|\"\"\"\"\"|_|\"\"\"\"\"|_|\"\"\"\"\"|_| \"\"\"\"|_| |_|\"\"\"\"\"|_|\"\"\"\"\"|\n" +
|
||||
"\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\"`-0-0-'\n" +
|
||||
"----------------------------------------------------------->"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 版本长度并不固定,比如beta版,所以需要特殊处理
|
||||
int width = 43;
|
||||
int blank = width - versionStr.length();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(":: version :: ")
|
||||
.append(versionStr);
|
||||
for (int i = 0; i < blank; i++) {
|
||||
sb.append(" ");
|
||||
}
|
||||
sb.append(">");
|
||||
if (iKunMode) {
|
||||
System.out.println("----------------------------------------------------------->");
|
||||
}
|
||||
System.out.println(":: project :: Easy-Es >");
|
||||
System.out.println(sb);
|
||||
System.out.println(":: home :: https://easy-es.cn/ >");
|
||||
System.out.println(":: community :: https://dromara.org/ >");
|
||||
System.out.println(wechatStr);
|
||||
System.out.println("----------------------------------------------------------->");
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> getDatasource() {
|
||||
String dynamic = this.applicationContext.getEnvironment().getRequiredProperty("easy-es.dynamic");
|
||||
Properties properties = new Properties();
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
try {
|
||||
properties.load(new java.io.StringReader(dynamic));
|
||||
properties.forEach((key, value) -> map.put(key.toString(), value.toString()));
|
||||
} catch (Exception e) {
|
||||
// Handle exception
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static RestHighLevelClient restHighLevelClient(EasyEsProperty easyEsConfigProperties) {
|
||||
// 处理地址
|
||||
String address = easyEsConfigProperties.getAddress();
|
||||
if (StringUtils.isEmpty(address)) {
|
||||
throw ExceptionUtils.eee("please config the es address");
|
||||
}
|
||||
if (!address.contains(COLON)) {
|
||||
throw ExceptionUtils.eee("the address must contains port and separate by ':'");
|
||||
}
|
||||
String schema = StringUtils.isEmpty(easyEsConfigProperties.getSchema())
|
||||
? DEFAULT_SCHEMA : easyEsConfigProperties.getSchema();
|
||||
List<HttpHost> hostList = new ArrayList<>();
|
||||
Arrays.stream(easyEsConfigProperties.getAddress().split(COMMA))
|
||||
.forEach(item -> hostList.add(new HttpHost(item.split(COLON)[0],
|
||||
Integer.parseInt(item.split(COLON)[1]), schema)));
|
||||
|
||||
// 转换成 HttpHost 数组
|
||||
HttpHost[] httpHost = hostList.toArray(new HttpHost[]{});
|
||||
|
||||
// 构建连接对象
|
||||
RestClientBuilder builder = RestClient.builder(httpHost);
|
||||
builder.setHttpClientConfigCallback(httpClientBuilder -> {
|
||||
// 设置心跳时间,最大连接数,最大连接路由
|
||||
Optional.ofNullable(easyEsConfigProperties.getKeepAliveMillis()).ifPresent(p -> httpClientBuilder.setKeepAliveStrategy((response, context) -> p));
|
||||
Optional.ofNullable(easyEsConfigProperties.getMaxConnTotal()).ifPresent(httpClientBuilder::setMaxConnTotal);
|
||||
Optional.ofNullable(easyEsConfigProperties.getMaxConnPerRoute()).ifPresent(httpClientBuilder::setMaxConnPerRoute);
|
||||
|
||||
// 设置账号密码
|
||||
String username = easyEsConfigProperties.getUsername();
|
||||
String password = easyEsConfigProperties.getPassword();
|
||||
if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(password)) {
|
||||
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
|
||||
credentialsProvider.setCredentials(AuthScope.ANY,
|
||||
new UsernamePasswordCredentials(username, password));
|
||||
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
|
||||
}
|
||||
|
||||
// https ssl ignore
|
||||
if (SchemaEnum.https.name().equals(schema)) {
|
||||
try {
|
||||
// 信任所有
|
||||
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true).build();
|
||||
SSLIOSessionStrategy sessionStrategy = new SSLIOSessionStrategy(sslContext, NoopHostnameVerifier.INSTANCE);
|
||||
httpClientBuilder.disableAuthCaching();
|
||||
httpClientBuilder.setSSLStrategy(sessionStrategy);
|
||||
} catch (Exception e) {
|
||||
LogUtils.error("restHighLevelClient build SSLContext exception: %s", e.getMessage());
|
||||
e.printStackTrace();
|
||||
throw ExceptionUtils.eee(e);
|
||||
}
|
||||
}
|
||||
return httpClientBuilder;
|
||||
});
|
||||
|
||||
// 设置超时时间之类的
|
||||
builder.setRequestConfigCallback(requestConfigBuilder -> {
|
||||
Optional.ofNullable(easyEsConfigProperties.getConnectTimeout()).ifPresent(requestConfigBuilder::setConnectTimeout);
|
||||
Optional.ofNullable(easyEsConfigProperties.getSocketTimeout()).ifPresent(requestConfigBuilder::setSocketTimeout);
|
||||
Optional.ofNullable(easyEsConfigProperties.getConnectionRequestTimeout())
|
||||
.ifPresent(requestConfigBuilder::setConnectionRequestTimeout);
|
||||
return requestConfigBuilder;
|
||||
});
|
||||
return new RestHighLevelClient(builder);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package org.dromara.easyes.spring;
|
||||
|
||||
import org.dromara.easyes.common.utils.EEVersionUtils;
|
||||
import org.dromara.easyes.common.utils.LogUtils;
|
||||
import org.dromara.easyes.spring.annotation.EsMapperScan;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.context.EnvironmentAware;
|
||||
import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
|
||||
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.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.dromara.easyes.common.constants.BaseEsConstants.*;
|
||||
|
||||
/**
|
||||
* 注册bean
|
||||
* <p>
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public class MapperScannerRegister implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
|
||||
private ResourceLoader resourceLoader;
|
||||
private Environment environment;
|
||||
|
||||
private static final String BASE_PACKAGE = "basePackage";
|
||||
private static final String INDEX_STRATEGY_FACTORY = "indexStrategyFactory";
|
||||
private static final String REST_HIGH_LEVEL_CLIENT_UTILS = "restHighLevelClientUtils";
|
||||
private static final String REST_HIGH_LEVEL_CLIENT_CONFIGURATION = "restHighLevelClientConfiguration";
|
||||
|
||||
@Override
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
|
||||
|
||||
|
||||
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
|
||||
.fromMap(importingClassMetadata.getAnnotationAttributes(EsMapperScan.class.getName()));
|
||||
// 默认已注解标记为主,如果没有则尝试寻找easy-es配置 scan
|
||||
if (mapperScanAttrs != null) {
|
||||
List<String> basePackages = Arrays.stream(mapperScanAttrs.getStringArray("value"))
|
||||
.filter(StringUtils::hasText).collect(Collectors.toList());
|
||||
// 注册bean
|
||||
registerBeanDefinitions(registry, generateBaseBeanName(importingClassMetadata, 0),
|
||||
StringUtils.toStringArray(basePackages));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过BeanDefinition注册bean,在bean注册前处理interface中bean参数问题,interface与普通bean不同,
|
||||
* 使用了jdk动态代理,需要确定注册的实际interface class,就需要通过BeanDefinition来追加属性,
|
||||
* 当前使用到了spring的构造
|
||||
* @param registry spring bean扫码注册器
|
||||
* @param basePackages 扫码的包
|
||||
*/
|
||||
void registerBeanDefinitions(BeanDefinitionRegistry registry, String beanName, String... basePackages) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
|
||||
builder.addPropertyValue(BASE_PACKAGE, String.join(",", basePackages));
|
||||
if (registry.containsBeanDefinition(INDEX_STRATEGY_FACTORY)) {
|
||||
builder.addPropertyValue(INDEX_STRATEGY_FACTORY, registry.getBeanDefinition(INDEX_STRATEGY_FACTORY));
|
||||
}
|
||||
// 如果自行注册了restHighLevelClientUtils,那么就添加bean属性
|
||||
if (registry.containsBeanDefinition(REST_HIGH_LEVEL_CLIENT_UTILS)) {
|
||||
builder.addPropertyValue(REST_HIGH_LEVEL_CLIENT_UTILS, registry
|
||||
.getBeanDefinition(REST_HIGH_LEVEL_CLIENT_UTILS));
|
||||
} else {
|
||||
// 没有注册restHighLevelClientUtils,那么尝试寻找easy-es-spring的restHighLevelClientConfiguration
|
||||
if (registry.containsBeanDefinition(REST_HIGH_LEVEL_CLIENT_CONFIGURATION)) {
|
||||
builder.addPropertyValue(REST_HIGH_LEVEL_CLIENT_UTILS, registry
|
||||
.getBeanDefinition(REST_HIGH_LEVEL_CLIENT_CONFIGURATION));
|
||||
}
|
||||
}
|
||||
|
||||
// for spring-native
|
||||
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
|
||||
}
|
||||
|
||||
private static String generateBaseBeanName(AnnotationMetadata importingClassMetadata, int index) {
|
||||
return importingClassMetadata.getClassName() + "#" + MapperScannerRegister.class.getSimpleName() + "#" + index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnvironment(Environment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package org.dromara.easyes.spring.annotation;
|
||||
|
||||
import org.dromara.easyes.spring.MapperScannerRegister;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 全局Mapper扫描注解
|
||||
* <p>
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.TYPE)
|
||||
@Import(MapperScannerRegister.class)
|
||||
public @interface EsMapperScan {
|
||||
String[] value();
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package org.dromara.easyes.spring.factory;
|
||||
|
||||
import org.dromara.easyes.common.constants.BaseEsConstants;
|
||||
import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum;
|
||||
import org.dromara.easyes.common.utils.Assert;
|
||||
import org.dromara.easyes.common.utils.ExceptionUtils;
|
||||
import org.dromara.easyes.core.service.AutoProcessIndexService;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 自动托管索引策略工厂
|
||||
* <p>
|
||||
* Copyright © 2022 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public class IndexStrategyFactory implements ApplicationContextAware, InitializingBean {
|
||||
/**
|
||||
* 预估初始策略工厂容量
|
||||
*/
|
||||
private static final Integer DEFAULT_SIZE = 4;
|
||||
/**
|
||||
* spring上下文
|
||||
*/
|
||||
private ApplicationContext applicationContext;
|
||||
/**
|
||||
* 策略容器
|
||||
*/
|
||||
private static final Map<Integer, AutoProcessIndexService> SERVICE_MAP = new HashMap<>(DEFAULT_SIZE);
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
Map<String, AutoProcessIndexService> beansOfType = this.applicationContext
|
||||
.getBeansOfType(AutoProcessIndexService.class);
|
||||
Assert.isTrue(beansOfType.isEmpty(), "AutoProcessIndexService must have implementation. " +
|
||||
"AutoProcessIndexService接口必须要有索引策略实现。");
|
||||
// 是否开启自动托管模式,默认开启
|
||||
ProcessIndexStrategyEnum strategy = this.applicationContext.getEnvironment()
|
||||
.getProperty(BaseEsConstants.INDEX_MODE, ProcessIndexStrategyEnum.class, ProcessIndexStrategyEnum.MANUAL);
|
||||
if (!ProcessIndexStrategyEnum.MANUAL.equals(strategy)) {
|
||||
// 将bean注册进工厂
|
||||
beansOfType.values().forEach(v -> SERVICE_MAP.putIfAbsent(v.getStrategyType(), v));
|
||||
}
|
||||
}
|
||||
|
||||
public AutoProcessIndexService getByStrategyType(Integer strategyType) {
|
||||
return Optional.ofNullable(SERVICE_MAP.get(strategyType))
|
||||
.orElseThrow(() -> ExceptionUtils.eee("no such service strategyType:{}", strategyType));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package org.dromara.easyes.spring.index;
|
||||
|
||||
import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum;
|
||||
import org.dromara.easyes.common.utils.LogUtils;
|
||||
import org.dromara.easyes.core.biz.CreateIndexParam;
|
||||
import org.dromara.easyes.core.biz.EntityInfo;
|
||||
import org.dromara.easyes.core.biz.EsIndexInfo;
|
||||
import org.dromara.easyes.core.service.AutoProcessIndexService;
|
||||
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
|
||||
import org.dromara.easyes.core.toolkit.IndexUtils;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
|
||||
/**
|
||||
* 自动非平滑托管索引实现类, 重建索引时原索引数据会被删除
|
||||
* <p>
|
||||
* Copyright © 2022 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public class AutoProcessIndexNotSmoothlyServiceImpl implements AutoProcessIndexService {
|
||||
|
||||
@Override
|
||||
public Integer getStrategyType() {
|
||||
return ProcessIndexStrategyEnum.NOT_SMOOTHLY.getStrategyType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processIndexAsync(Class<?> entityClass, RestHighLevelClient client) {
|
||||
LogUtils.info("===> Not smoothly process index mode activated");
|
||||
IndexUtils.supplyAsync(this::process, entityClass, client);
|
||||
}
|
||||
|
||||
private boolean process(Class<?> entityClass, RestHighLevelClient client) {
|
||||
EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass);
|
||||
// 是否存在索引
|
||||
boolean existsIndex = IndexUtils.existsIndexWithRetry(entityInfo, client);
|
||||
if (existsIndex) {
|
||||
// 更新
|
||||
LogUtils.info("===> Index exists, automatically updating index by easy-es...");
|
||||
return doUpdateIndex(entityInfo, entityClass, client);
|
||||
} else {
|
||||
// 新建
|
||||
LogUtils.info("===> Index not exists, automatically creating index by easy-es...");
|
||||
return doCreateIndex(entityInfo, entityClass, client);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doUpdateIndex(EntityInfo entityInfo, Class<?> clazz, RestHighLevelClient client) {
|
||||
// 获取索引信息
|
||||
EsIndexInfo esIndexInfo = IndexUtils.getIndexInfo(client, entityInfo.getRetrySuccessIndexName());
|
||||
|
||||
// 索引是否有变化 若有则直接删除旧索引,创建新索引 若无则直接返回托管成功
|
||||
boolean isIndexNeedChange = IndexUtils.isIndexNeedChange(esIndexInfo, entityInfo, clazz);
|
||||
if (!isIndexNeedChange) {
|
||||
LogUtils.info("===> index has nothing changed");
|
||||
entityInfo.setIndexName(entityInfo.getRetrySuccessIndexName());
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
// 直接删除旧索引
|
||||
IndexUtils.deleteIndex(client, entityInfo.getRetrySuccessIndexName());
|
||||
|
||||
// 初始化创建索引参数
|
||||
CreateIndexParam createIndexParam = IndexUtils.getCreateIndexParam(entityInfo, clazz);
|
||||
|
||||
// 执行创建
|
||||
return IndexUtils.createIndex(client, entityInfo, createIndexParam);
|
||||
}
|
||||
|
||||
private boolean doCreateIndex(EntityInfo entityInfo, Class<?> clazz, RestHighLevelClient client) {
|
||||
// 初始化创建索引参数
|
||||
CreateIndexParam createIndexParam = IndexUtils.getCreateIndexParam(entityInfo, clazz);
|
||||
|
||||
// 执行创建
|
||||
return IndexUtils.createIndex(client, entityInfo, createIndexParam);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
package org.dromara.easyes.spring.index;
|
||||
|
||||
import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum;
|
||||
import org.dromara.easyes.common.utils.LogUtils;
|
||||
import org.dromara.easyes.core.biz.CreateIndexParam;
|
||||
import org.dromara.easyes.core.biz.EntityInfo;
|
||||
import org.dromara.easyes.core.biz.EsIndexInfo;
|
||||
import org.dromara.easyes.core.service.AutoProcessIndexService;
|
||||
import org.dromara.easyes.core.toolkit.EntityInfoHelper;
|
||||
import org.dromara.easyes.core.toolkit.IndexUtils;
|
||||
import org.elasticsearch.client.RestHighLevelClient;
|
||||
|
||||
import static org.dromara.easyes.common.constants.BaseEsConstants.S1_SUFFIX;
|
||||
import static org.dromara.easyes.common.constants.BaseEsConstants.SO_SUFFIX;
|
||||
|
||||
/**
|
||||
* 自动平滑托管索引实现类,本框架默认模式,过程零停机,数据会自动转移至新索引
|
||||
* <p>
|
||||
* Copyright © 2022 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public class AutoProcessIndexSmoothlyServiceImpl implements AutoProcessIndexService {
|
||||
@Override
|
||||
public Integer getStrategyType() {
|
||||
return ProcessIndexStrategyEnum.SMOOTHLY.getStrategyType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processIndexAsync(Class<?> entityClass, RestHighLevelClient client) {
|
||||
LogUtils.info("===> Smoothly process index mode activated");
|
||||
IndexUtils.supplyAsync(this::process, entityClass, client);
|
||||
}
|
||||
|
||||
private synchronized boolean process(Class<?> entityClass, RestHighLevelClient client) {
|
||||
EntityInfo entityInfo = EntityInfoHelper.getEntityInfo(entityClass);
|
||||
|
||||
// 索引是否已存在
|
||||
boolean existsIndex = IndexUtils.existsIndexWithRetryAndSetActiveIndex(entityInfo, client);
|
||||
if (existsIndex) {
|
||||
// 更新
|
||||
LogUtils.info("===> Index exists, automatically updating index by easy-es...");
|
||||
return doUpdateIndex(entityInfo, entityClass, client);
|
||||
} else {
|
||||
// 新建
|
||||
LogUtils.info("===> Index not exists, automatically creating index by easy-es...");
|
||||
return doCreateIndex(entityInfo, entityClass, client);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean doUpdateIndex(EntityInfo entityInfo, Class<?> clazz, RestHighLevelClient client) {
|
||||
// 获取索引信息
|
||||
EsIndexInfo esIndexInfo = IndexUtils.getIndexInfo(client, entityInfo.getIndexName());
|
||||
|
||||
// 是否存在默认别名,若无则给添加
|
||||
if (!esIndexInfo.getHasDefaultAlias()) {
|
||||
IndexUtils.addDefaultAlias(client, entityInfo.getIndexName());
|
||||
}
|
||||
|
||||
// 索引是否有变化 若有则创建新索引并无感迁移, 若无则直接返回托管成功
|
||||
boolean isIndexNeedChange = IndexUtils.isIndexNeedChange(esIndexInfo, entityInfo, clazz);
|
||||
if (!isIndexNeedChange) {
|
||||
LogUtils.info("===> index has nothing changed");
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
// 创建新索引
|
||||
String releaseIndexName = generateReleaseIndexName(entityInfo.getIndexName());
|
||||
entityInfo.setReleaseIndexName(releaseIndexName);
|
||||
boolean isCreateIndexSuccess = doCreateIndex(entityInfo, clazz, client);
|
||||
if (!isCreateIndexSuccess) {
|
||||
LogUtils.error("create release index failed", "releaseIndex:" + releaseIndexName);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
// 迁移数据至新创建的索引
|
||||
boolean isDataMigrationSuccess = doDataMigration(entityInfo.getIndexName(), releaseIndexName, entityInfo.getMaxResultWindow(), client);
|
||||
if (!isDataMigrationSuccess) {
|
||||
LogUtils.error("migrate data failed", "oldIndex:" + entityInfo.getIndexName(), "releaseIndex:" + releaseIndexName);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
// 原子操作 切换别名:将默认别名关联至新索引,并将旧索引的默认别名移除
|
||||
boolean isChangeAliasSuccess = IndexUtils.changeAliasAtomic(client, entityInfo.getIndexName(), releaseIndexName);
|
||||
if (!isChangeAliasSuccess) {
|
||||
LogUtils.error("change alias atomically failed", "oldIndex:" + entityInfo.getIndexName(), "releaseIndex:" + releaseIndexName);
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
// 删除旧索引
|
||||
boolean isDeletedIndexSuccess = IndexUtils.deleteIndex(client, entityInfo.getIndexName());
|
||||
if (!isDeletedIndexSuccess) {
|
||||
LogUtils.error("delete old index failed", "oldIndex:" + entityInfo.getIndexName());
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
|
||||
// 用最新索引覆盖缓存中的老索引
|
||||
entityInfo.setIndexName(releaseIndexName);
|
||||
|
||||
// 将新索引名称记录至ee-distribute-lock索引中,以便在分布式环境下其它机器能够感知到
|
||||
IndexUtils.saveReleaseIndex(releaseIndexName, client);
|
||||
|
||||
// done.
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
private String generateReleaseIndexName(String oldIndexName) {
|
||||
if (oldIndexName.endsWith(SO_SUFFIX)) {
|
||||
return oldIndexName.split(SO_SUFFIX)[0] + S1_SUFFIX;
|
||||
} else if (oldIndexName.endsWith(S1_SUFFIX)) {
|
||||
return oldIndexName.split(S1_SUFFIX)[0] + SO_SUFFIX;
|
||||
} else {
|
||||
return oldIndexName + SO_SUFFIX;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doDataMigration(String oldIndexName, String releaseIndexName, Integer maxResultWindow, RestHighLevelClient client) {
|
||||
return IndexUtils.reindex(client, oldIndexName, releaseIndexName, maxResultWindow);
|
||||
}
|
||||
|
||||
private boolean doCreateIndex(EntityInfo entityInfo, Class<?> clazz, RestHighLevelClient client) {
|
||||
// 初始化创建索引参数
|
||||
CreateIndexParam createIndexParam = IndexUtils.getCreateIndexParam(entityInfo, clazz);
|
||||
// 执行创建
|
||||
return IndexUtils.createIndex(client, entityInfo, createIndexParam);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
package org.dromara.easyes.spring.property;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.dromara.easyes.annotation.rely.FieldStrategy;
|
||||
import org.dromara.easyes.annotation.rely.IdType;
|
||||
import org.dromara.easyes.annotation.rely.RefreshPolicy;
|
||||
import org.dromara.easyes.common.constants.BaseEsConstants;
|
||||
import org.dromara.easyes.common.enums.ProcessIndexStrategyEnum;
|
||||
import org.dromara.easyes.common.enums.SchemaEnum;
|
||||
import org.dromara.easyes.common.property.GlobalConfig;
|
||||
import org.dromara.easyes.common.utils.StringUtils;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.dromara.easyes.common.constants.BaseEsConstants.EMPTY_STR;
|
||||
|
||||
/**
|
||||
* easy-es基础配置项 考虑到spring的场景,有些参数不是必须配置
|
||||
* 基本类型就会出现默认值的情况 所以为了要有null值出现,这里采用包装类型
|
||||
* <p>
|
||||
* Copyright © 2022 xpc1024 All Rights Reserved
|
||||
**/
|
||||
@Data
|
||||
public class EasyEsProperty {
|
||||
|
||||
/**
|
||||
* 是否开启easy-es 默认开启
|
||||
*/
|
||||
private Boolean enable;
|
||||
/**
|
||||
* 是否开启easy-es LOGO BANNER的打印
|
||||
*/
|
||||
private Boolean banner;
|
||||
/**
|
||||
* es client address es客户端地址
|
||||
*/
|
||||
private String address;
|
||||
/**
|
||||
* schema 模式
|
||||
*/
|
||||
private String schema;
|
||||
/**
|
||||
* username of es 用户名,可缺省
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* password of es 密码,可缺省
|
||||
*/
|
||||
private String password;
|
||||
/**
|
||||
* maxConnectTotal 最大连接数
|
||||
*/
|
||||
private Integer maxConnTotal;
|
||||
/**
|
||||
* maxConnectPerRoute 最大连接路由数
|
||||
*/
|
||||
private Integer maxConnPerRoute;
|
||||
/**
|
||||
* connectTimeout timeUnit:millis 连接超时时间 单位毫秒
|
||||
*/
|
||||
private Integer connectTimeout;
|
||||
/**
|
||||
* socketTimeout timeUnit:millis 通讯超时时间 单位毫秒 默认10分钟 以免小白踩坑平滑模式下数据量过大迁移超时失败
|
||||
*/
|
||||
private Integer socketTimeout;
|
||||
/***
|
||||
* 保持心跳时间 timeUnit:millis 单位毫秒 默认30秒 防止小白白出现服务首次调用不成时不知所措
|
||||
*/
|
||||
private Integer keepAliveMillis;
|
||||
/**
|
||||
* connectionRequestTimeout timeUnit:millis 连接请求超时时间 单位毫秒
|
||||
*/
|
||||
private Integer connectionRequestTimeout;
|
||||
/**
|
||||
* global config 全局配置
|
||||
*/
|
||||
private GlobalConfig globalConfig;
|
||||
/**
|
||||
* 配置多动态数据源key datasource id
|
||||
*/
|
||||
private Map<String, EasyEsProperty> datasource = new HashMap<>();
|
||||
|
||||
public EasyEsProperty(Environment environment) {
|
||||
this.setEnable(environment.getProperty(BaseEsConstants.ENABLE_PREFIX, Boolean.class, true));
|
||||
this.setBanner(environment.getProperty(BaseEsConstants.ENABLE_BANNER, Boolean.class, true));
|
||||
this.setAddress(environment.getProperty("easy-es.address", "127.0.0.1:9200"));
|
||||
this.setSchema(environment.getProperty("easy-es.schema", SchemaEnum.http.name()));
|
||||
this.setUsername(environment.getProperty("easy-es.username"));
|
||||
this.setPassword(environment.getProperty("easy-es.password"));
|
||||
this.setMaxConnTotal(environment.getProperty("easy-es.max-conn-total", Integer.class));
|
||||
this.setMaxConnPerRoute(environment.getProperty("easy-es.max-conn-per-route", Integer.class));
|
||||
this.setConnectTimeout(environment.getProperty("easy-es.connect-timeout", Integer.class));
|
||||
this.setSocketTimeout(environment.getProperty("easy-es.socket-timeout", Integer.class, 600000));
|
||||
this.setKeepAliveMillis(environment.getProperty("easy-es.keep-alive-millis", Integer.class, 30000));
|
||||
this.setConnectionRequestTimeout(environment.getProperty("easy-es.connection-request-timeout", Integer.class));
|
||||
GlobalConfig globalConfig = new GlobalConfig();
|
||||
globalConfig.setPrintDsl(environment.getProperty("easy-es.global-config.print-dsl", Boolean.class, true));
|
||||
globalConfig.setIKunMode(environment.getProperty("easy-es.global-config.i-kun-mode", Boolean.class, false));
|
||||
globalConfig.setProcessIndexMode(environment.getProperty("easy-es.global-config.process-index-mode",
|
||||
ProcessIndexStrategyEnum.class, ProcessIndexStrategyEnum.MANUAL));
|
||||
globalConfig.setDistributed(environment.getProperty("easy-es.global-config.distributed", Boolean.class, true));
|
||||
globalConfig.setReindexTimeOutHours(environment.getProperty("easy-es.global-config.reindex-time-out-hours",
|
||||
Integer.class, 72));
|
||||
globalConfig.setAsyncProcessIndexBlocking(environment.getProperty("easy-es.global-config.async-process-index-blocking",
|
||||
Boolean.class, true));
|
||||
globalConfig.setActiveReleaseIndexMaxRetry(environment.getProperty("easy-es.global-config.active-release-index-max-retry",
|
||||
Integer.class, 4320));
|
||||
globalConfig.setActiveReleaseIndexFixedDelay(environment.getProperty("easy-es.global-config.active-release-index-fixed-delay",
|
||||
Integer.class, 60));
|
||||
GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();
|
||||
dbConfig.setIndexPrefix(environment.getProperty("easy-es.global-config.db-config.index-prefix", EMPTY_STR));
|
||||
dbConfig.setMapUnderscoreToCamelCase(environment.getProperty("easy-es.global-config.db-config.map-underscore-to-camel-case",
|
||||
Boolean.class, false));
|
||||
dbConfig.setIdType(environment.getProperty("easy-es.global-config.db-config.id-type", IdType.class, IdType.NONE));
|
||||
dbConfig.setFieldStrategy(environment.getProperty("easy-es.global-config.db-config.field-strategy",
|
||||
FieldStrategy.class, FieldStrategy.NOT_NULL));
|
||||
dbConfig.setEnableTrackTotalHits(environment.getProperty("easy-es.global-config.db-config.enable-track-total-hits",
|
||||
Boolean.class, true));
|
||||
dbConfig.setRefreshPolicy(environment.getProperty("easy-es.global-config.db-config.refresh-policy",
|
||||
RefreshPolicy.class, RefreshPolicy.NONE));
|
||||
dbConfig.setBatchUpdateThreshold(environment.getProperty("easy-es.global-config.db-config.batch-update-threshold",
|
||||
Integer.class, 10000));
|
||||
dbConfig.setSmartAddKeywordSuffix(environment.getProperty("easy-es.global-config.db-config.smart-add-keyword-suffix",
|
||||
Boolean.class, true));
|
||||
globalConfig.setDbConfig(dbConfig);
|
||||
this.setGlobalConfig(globalConfig);
|
||||
|
||||
// 设置多数据源
|
||||
}
|
||||
|
||||
public EasyEsProperty() {
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package org.dromara.easyes.test;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 启动类
|
||||
* <p>
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
@SpringBootApplication
|
||||
public class TestEasyEsApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestEasyEsApplication.class, args);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
||||
package org.dromara.easyes.test.config;
|
||||
|
||||
import org.dromara.easyes.spring.annotation.EsMapperScan;
|
||||
import org.dromara.easyes.spring.factory.IndexStrategyFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* @author MoJie
|
||||
* @since 2.0
|
||||
*/
|
||||
//@Configuration
|
||||
//@EsMapperScan("org.dromara.easyes.test.mapper")
|
||||
public class TestConfig {
|
||||
|
||||
@Bean
|
||||
public IndexStrategyFactory indexStrategyFactory() {
|
||||
return new IndexStrategyFactory();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package org.dromara.easyes.test.entity;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.easyes.annotation.IndexField;
|
||||
import org.dromara.easyes.annotation.IndexId;
|
||||
import org.dromara.easyes.annotation.rely.FieldType;
|
||||
|
||||
|
||||
/**
|
||||
* 作者 数据模型 Document的子文档,Document是其父文档
|
||||
* <p>
|
||||
* Copyright © 2024 xpc1024 All Rights Reserved
|
||||
**/
|
||||
@Data
|
||||
public class Author {
|
||||
/**
|
||||
* 作者id
|
||||
*/
|
||||
@IndexId
|
||||
private String authorId;
|
||||
/**
|
||||
* 作者姓名
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.KEYWORD)
|
||||
private String authorName;
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package org.dromara.easyes.test.entity;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.easyes.annotation.IndexField;
|
||||
import org.dromara.easyes.annotation.rely.FieldType;
|
||||
|
||||
|
||||
/**
|
||||
* es 评论 数据模型 Document的子文档,Document是其父文档
|
||||
* <p>
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
@Data
|
||||
public class Comment {
|
||||
/**
|
||||
* 评论id
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 评论内容
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.KEYWORD)
|
||||
private String commentContent;
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package org.dromara.easyes.test.entity;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.easyes.annotation.IndexField;
|
||||
import org.dromara.easyes.annotation.IndexId;
|
||||
import org.dromara.easyes.annotation.rely.FieldType;
|
||||
|
||||
|
||||
/**
|
||||
* 联系方式 数据模型 Author的子文档,Author是其父文档,Document是其爷文档
|
||||
* <p>
|
||||
* Copyright © 2024 xpc1024 All Rights Reserved
|
||||
**/
|
||||
@Data
|
||||
public class Contact {
|
||||
/**
|
||||
* 联系人id
|
||||
*/
|
||||
@IndexId
|
||||
private String contactId;
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.TEXT)
|
||||
private String address;
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
package org.dromara.easyes.test.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.dromara.easyes.annotation.*;
|
||||
import org.dromara.easyes.annotation.rely.*;
|
||||
import org.dromara.easyes.test.settings.MySettingsProvider;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* es 数据模型 其中Join父子类型结构如下所示
|
||||
* <pre>
|
||||
* Document
|
||||
* / \
|
||||
* Comment Author
|
||||
* \
|
||||
* Contact
|
||||
* </pre>
|
||||
* <p>
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
@Data
|
||||
@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)})
|
||||
public class Document {
|
||||
/**
|
||||
* es中的唯一id,字段名随便起,我这里演示用esId,你也可以用id(推荐),bizId等.
|
||||
* 如果你想自定义es中的id为你提供的id,比如MySQL中的id,请将注解中的type指定为customize或直接在全局配置文件中指定,如此id便支持任意数据类型)
|
||||
*/
|
||||
@IndexId(type = IdType.CUSTOMIZE)
|
||||
private String esId;
|
||||
/**
|
||||
* 文档标题,不指定类型默认被创建为keyword类型,可进行精确查询
|
||||
*/
|
||||
private String title;
|
||||
/**
|
||||
* 副标题
|
||||
*/
|
||||
private String subTitle;
|
||||
/**
|
||||
* 文档内容,指定了类型及存储/查询分词器
|
||||
*/
|
||||
@HighLight(mappingField = "highlightContent", fragmentSize = 10, numberOfFragments = 2, requireFieldMatch = false, preTag = "<em>", postTag = "</em>")
|
||||
@IndexField(fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART, searchAnalyzer = Analyzer.IK_SMART)
|
||||
private String content;
|
||||
/**
|
||||
* 作者 加@TableField注解,并指明strategy = FieldStrategy.NOT_EMPTY 表示更新的时候的策略为 创建者不为空字符串时才更新
|
||||
*/
|
||||
@IndexField(strategy = FieldStrategy.NOT_EMPTY, fieldType = FieldType.KEYWORD_TEXT, analyzer = Analyzer.IK_SMART)
|
||||
private String creator;
|
||||
/**
|
||||
* 可以聚合的text类型,字段名字随便取,注解中指定fieldData=true后text类型也可以支持聚合
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.TEXT, fieldData = true)
|
||||
private String filedData;
|
||||
/**
|
||||
* ip字段
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.IP)
|
||||
private String ipAddress;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.DATE, dateFormat = "yyyy-MM-dd HH:mm:ss")
|
||||
private String gmtCreate;
|
||||
/**
|
||||
* es中实际不存在的字段,但模型中加了,为了不和es映射,可以在此类型字段上加上 注解@TableField,并指明exist=false
|
||||
*/
|
||||
@IndexField(exist = false)
|
||||
private String notExistsField;
|
||||
/**
|
||||
* 地理位置经纬度坐标 例如: "40.13933715136454,116.63441990026217"
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.GEO_POINT)
|
||||
private String location;
|
||||
/**
|
||||
* 图形(例如圆心,矩形)
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.GEO_SHAPE)
|
||||
private String geoLocation;
|
||||
/**
|
||||
* 自定义字段名称
|
||||
*/
|
||||
@HighLight(preTag = "<nb>", postTag = "</nb>")
|
||||
@IndexField(value = "wu-la", fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART, searchAnalyzer = Analyzer.IK_SMART)
|
||||
private String customField;
|
||||
|
||||
/**
|
||||
* 忽略大小写字段,此字段的值用eq查询(termQuery)时,不区分大小写,值得注意的是es中keyword类型字段才支持忽略大小写
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.KEYWORD, ignoreCase = true)
|
||||
private String caseTest;
|
||||
|
||||
/**
|
||||
* 高亮返回值被映射的字段
|
||||
*/
|
||||
private String highlightContent;
|
||||
/**
|
||||
* 文档点赞数
|
||||
*/
|
||||
private Integer starNum;
|
||||
/**
|
||||
* 此字段存null值,测试isNull时用
|
||||
*/
|
||||
private String nullField;
|
||||
/**
|
||||
* 嵌套类型 注意,务必像下面示例一样指定类型为nested及其nested class,否则会导致框架无法正常运行
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.NESTED, nestedClass = User.class)
|
||||
private List<User> users;
|
||||
|
||||
/**
|
||||
* es返回的得分字段,字段名字随便取,只要加了@Score注解即可
|
||||
*/
|
||||
@Score(decimalPlaces = 2)
|
||||
private Float score;
|
||||
/**
|
||||
* es返回的距离,字段名字随便取,距离单位以用户在序器中指定的为准,不指定es默认为:米
|
||||
*/
|
||||
@Distance(decimalPlaces = 1)
|
||||
private Double distance;
|
||||
|
||||
/**
|
||||
* es返回的距离2,有N个排序器就有N个排序返回的距离 参考cn.easyes.test.all.AllTest#testOrderByDistanceMulti
|
||||
*/
|
||||
@Distance(decimalPlaces = 2)
|
||||
private Double distance2;
|
||||
|
||||
/**
|
||||
* 浮点数,可指定缩放因子,不指定默认值为100
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.SCALED_FLOAT, scalingFactor = 101)
|
||||
private BigDecimal bigNum;
|
||||
|
||||
/**
|
||||
* 复合字段,此注解和SpringData中的MultiField用法类似 适用于对同一个字段通过多种分词器检索的场景
|
||||
*/
|
||||
@HighLight
|
||||
@MultiIndexField(mainIndexField = @IndexField(fieldType = FieldType.KEYWORD),
|
||||
otherIndexFields = {@InnerIndexField(suffix = "zh", fieldType = FieldType.TEXT, analyzer = Analyzer.IK_SMART),
|
||||
@InnerIndexField(suffix = "pinyin", fieldType = FieldType.TEXT, analyzer = Analyzer.PINYIN)})
|
||||
private String multiField;
|
||||
/**
|
||||
* 英文名
|
||||
*/
|
||||
private String english;
|
||||
|
||||
/**
|
||||
* 稠密向量类型,dims 非负 最大为2048
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.DENSE_VECTOR, dims = 3)
|
||||
private double[] vector;
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package org.dromara.easyes.test.entity;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.easyes.annotation.HighLight;
|
||||
import org.dromara.easyes.annotation.IndexField;
|
||||
import org.dromara.easyes.annotation.InnerIndexField;
|
||||
import org.dromara.easyes.annotation.MultiIndexField;
|
||||
import org.dromara.easyes.annotation.rely.Analyzer;
|
||||
import org.dromara.easyes.annotation.rely.FieldType;
|
||||
|
||||
/**
|
||||
* 问答
|
||||
*
|
||||
* @ProductName: Hundsun HEP
|
||||
* @ProjectName: easy-es
|
||||
* @Package: com.xpc.easyes.sample.entity
|
||||
* @Description: note
|
||||
* @Author: xingpc37977
|
||||
* @Date: 2022/5/12 17:18
|
||||
* @UpdateUser: xingpc37977
|
||||
* @UpdateDate: 2022/5/12 17:18
|
||||
* @UpdateRemark: The modified content
|
||||
* @Version: 1.0
|
||||
* <p>
|
||||
* Copyright © 2022 Hundsun Technologies Inc. All Rights Reserved
|
||||
**/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Faq {
|
||||
/**
|
||||
* 问题 高亮内容直接覆盖在原字段值上进行返回,故不需要指定高亮注解中的mappingField
|
||||
*/
|
||||
@HighLight
|
||||
@MultiIndexField(mainIndexField = @IndexField,
|
||||
otherIndexFields = {
|
||||
@InnerIndexField(suffix = "ik", fieldType = FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD),
|
||||
@InnerIndexField(suffix = "py", fieldType = FieldType.TEXT, analyzer = Analyzer.PINYIN)
|
||||
})
|
||||
private String faqName;
|
||||
|
||||
/**
|
||||
* 答案
|
||||
*/
|
||||
@IndexField(value = "answer")
|
||||
private String faqAnswer;
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package org.dromara.easyes.test.entity;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.easyes.annotation.HighLight;
|
||||
import org.dromara.easyes.annotation.IndexField;
|
||||
import org.dromara.easyes.annotation.InnerIndexField;
|
||||
import org.dromara.easyes.annotation.MultiIndexField;
|
||||
import org.dromara.easyes.annotation.rely.Analyzer;
|
||||
import org.dromara.easyes.annotation.rely.FieldType;
|
||||
import org.dromara.easyes.test.entity.Faq;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* es 嵌套类型
|
||||
* <p>
|
||||
* Copyright © 2022 xpc1024 All Rights Reserved
|
||||
**/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class User {
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
@HighLight(mappingField = "highlightUsername")
|
||||
@MultiIndexField(mainIndexField = @IndexField("user_name"),
|
||||
otherIndexFields = {
|
||||
@InnerIndexField(suffix = "ik", fieldType = FieldType.TEXT, analyzer = Analyzer.IK_MAX_WORD),
|
||||
@InnerIndexField(suffix = "py", fieldType = FieldType.TEXT, analyzer = Analyzer.PINYIN)
|
||||
})
|
||||
private String username;
|
||||
/**
|
||||
* 年龄
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.INTEGER)
|
||||
private Integer age;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.KEYWORD)
|
||||
private String password;
|
||||
/**
|
||||
* 多级嵌套
|
||||
*/
|
||||
@IndexField(fieldType = FieldType.NESTED, nestedClass = Faq.class)
|
||||
private Set<Faq> faqs;
|
||||
/**
|
||||
* 高亮显示的内容
|
||||
*/
|
||||
private String highlightUsername;
|
||||
|
||||
public User(String username, Integer age, String password, Set<Faq> faqs) {
|
||||
this.username = username;
|
||||
this.age = age;
|
||||
this.password = password;
|
||||
this.faqs = faqs;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package org.dromara.easyes.test.generated;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.easyes.annotation.IndexName;
|
||||
import org.dromara.easyes.annotation.Settings;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@IndexName("EasyesDocument")
|
||||
@Settings(shardsNum = 3, replicasNum = 2)
|
||||
@Data
|
||||
public class EasyesDocument {
|
||||
// Fields
|
||||
private String authorName;
|
||||
private BigDecimal bigNum;
|
||||
private Date gmtCreate;
|
||||
private String caseTest;
|
||||
private String creator;
|
||||
private String nullField;
|
||||
private String address;
|
||||
private String geoLocation;
|
||||
private String subTitle;
|
||||
private String multiField;
|
||||
private String wula;
|
||||
private Integer starNum;
|
||||
private String ipAddress;
|
||||
private String title;
|
||||
private String content;
|
||||
private List<User> users;
|
||||
private String filedData;
|
||||
private String english;
|
||||
private String commentContent;
|
||||
private String location;
|
||||
private Object vector;
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
package org.dromara.easyes.test.generated;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Faq {
|
||||
// Fields
|
||||
private String answer;
|
||||
private String faqName;
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package org.dromara.easyes.test.generated;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class User {
|
||||
// Fields
|
||||
private List<Faq> faqs;
|
||||
private String password;
|
||||
private String userName;
|
||||
private Integer age;
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package org.dromara.easyes.test.mapper;
|
||||
|
||||
|
||||
import org.dromara.easyes.core.kernel.BaseEsMapper;
|
||||
import org.dromara.easyes.test.entity.Author;
|
||||
|
||||
/**
|
||||
* 父子类型-子文档的mapper
|
||||
* <p>
|
||||
* Copyright © 2024 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public interface AuthorMapper extends BaseEsMapper<Author> {
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package org.dromara.easyes.test.mapper;
|
||||
|
||||
|
||||
import org.dromara.easyes.core.kernel.BaseEsMapper;
|
||||
import org.dromara.easyes.test.entity.Comment;
|
||||
|
||||
/**
|
||||
* 父子类型-子文档的mapper
|
||||
* <p>
|
||||
* Copyright © 2022 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public interface CommentMapper extends BaseEsMapper<Comment> {
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package org.dromara.easyes.test.mapper;
|
||||
|
||||
|
||||
import org.dromara.easyes.core.kernel.BaseEsMapper;
|
||||
import org.dromara.easyes.test.entity.Contact;
|
||||
|
||||
/**
|
||||
* 父子类型-子文档的mapper
|
||||
* <p>
|
||||
* Copyright © 2024 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public interface ContactMapper extends BaseEsMapper<Contact> {
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
package org.dromara.easyes.test.mapper;
|
||||
|
||||
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
|
||||
import org.dromara.easyes.core.kernel.BaseEsMapper;
|
||||
import org.dromara.easyes.core.kernel.EsWrappers;
|
||||
import org.dromara.easyes.test.entity.Document;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* mapper 相当于Mybatis-plus的mapper
|
||||
* <p>
|
||||
* Copyright © 2021 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public interface DocumentMapper extends BaseEsMapper<Document> {
|
||||
/**
|
||||
* 演示mapper中添加default方法
|
||||
*
|
||||
* @return document列表
|
||||
*/
|
||||
default List<Document> testDefaultMethod() {
|
||||
LambdaEsQueryWrapper<Document> wrapper = EsWrappers.lambdaQuery(Document.class)
|
||||
.eq(Document::getTitle, "测试文档4").match(Document::getContent, "内容");
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package org.dromara.easyes.test.settings;
|
||||
|
||||
import org.dromara.easyes.annotation.rely.DefaultSettingsProvider;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 由于es索引的settings灵活多变,框架只能针对一部分场景作简化,其余场景需要用户自定义实现
|
||||
* <p>
|
||||
* Copyright © 2024 xpc1024 All Rights Reserved
|
||||
**/
|
||||
public class MySettingsProvider extends DefaultSettingsProvider {
|
||||
@Override
|
||||
public Map<String, Object> getSettings() {
|
||||
// TODO 这里可以自定义你的settings实现,将自定义的settings置入map并返回即可
|
||||
Map<String, Object> mySettings = new HashMap<>();
|
||||
// 例如指定查询操作的慢日志阈值为30秒,当查询操作的执行时间超过此阈值时,Elasticsearch会记录相应的慢日志并发出警告
|
||||
mySettings.put("index.search.slowlog.threshold.query.warn", "30s");
|
||||
return mySettings;
|
||||
}
|
||||
}
|
||||
10
easy-es-spring/src/test/resources/easy-es.properties
Normal file
10
easy-es-spring/src/test/resources/easy-es.properties
Normal file
@ -0,0 +1,10 @@
|
||||
easy-es.keep-alive-millis=18000
|
||||
easy-es.global-config.i-kun-mode=true
|
||||
easy-es.global-config.process-index-mode=manual
|
||||
easy-es.global-config.async-process-index-blocking=true
|
||||
easy-es.global-config.print-dsl=true
|
||||
easy-es.global-config.db-config.map-underscore-to-camel-case=true
|
||||
easy-es.global-config.db-config.id-type=customize
|
||||
easy-es.global-config.db-config.field-strategy=not_empty
|
||||
easy-es.global-config.db-config.refresh-policy=immediate
|
||||
easy-es.global-config.db-config.enable-track-total-hits=true
|
||||
18
easy-es-spring/src/test/resources/spring-context.xml
Normal file
18
easy-es-spring/src/test/resources/spring-context.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:context="http://www.springframework.org/schema/context"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context
|
||||
http://www.springframework.org/schema/context/spring-context.xsd">
|
||||
|
||||
<context:property-placeholder location="classpath:easy-es.properties" />
|
||||
|
||||
<bean id="indexStrategyFactory" class="org.dromara.easyes.spring.factory.IndexStrategyFactory"/>
|
||||
|
||||
<!-- easy-es配置 -->
|
||||
<bean id="mapperScannerConfigurer" class="org.dromara.easyes.spring.MapperScannerConfigurer">
|
||||
<property name="basePackage" value="org.dromara.easyes.test.mapper"/>
|
||||
<property name="indexStrategyFactory" ref="indexStrategyFactory"/>
|
||||
</bean>
|
||||
</beans>
|
||||
Loading…
x
Reference in New Issue
Block a user