最近公司專案中 有需要用ElasticSearch (后續簡稱ES) 集成 SQL 查詢功能,并可以按照請求引數動態切換目標資料源,同事找到我幫忙實作該功能,以前沒做過,只好趕鴨子上架,
網上很多資料不全,瞎琢磨半天終于完成,記錄了一些實作程序中踩過的坑,便于大家借鑒,
我們測驗環境部署的是 ElasticSearch6.8.2 ,對應需要使用的jar需要是同版本的x-pack-sql-jdbc.jar 否則會報版本不一致錯誤.
不過該功能的開通需要鉑金會員或者自己破解,具體的破解方案可以看看其他文章,以下介紹代碼的具體實作.
切換資料源部分有參考下方鏈接代碼,
https://blog.csdn.net/hekf2010/article/details/81155778
1. application.properties配置
server.port=6666
#主資料源
spring.datasource.url=jdbc:es://http://10.0.75.20:9200/
#es 從資料源 es1,es2
slave.datasource.names=es1,es2
#es1
slave.datasource.es1.url=jdbc:es://http://10.0.75.21:9200/
#es2
slave.datasource.es2.url=jdbc:es://http://10.0.75.22:9200/
#mapper.xml檔案
mybatis.mapper-locations=classpath:mapper/*.xml
#物體類包
mybatis.type-aliases-package=com.kunlun.es.vo
2. 注冊動態資料源.
PS:這個地方一開始以為要添加ES db驅動的,后面查看原始碼之后發現,這貨壓根就不需要添加EsDriver
import org.apache.log4j.Logger;
import org.elasticsearch.xpack.sql.jdbc.EsDataSource;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotationMetadata;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Author zhaozhiguo
* @Date 2020-11-04
* @Description 注冊動態資料源
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private Logger logger = Logger.getLogger(DynamicDataSourceRegister.class);
/***默認資料源***/
private DataSource deftDataSource;
/***自定義資料源***/
private Map<String, DataSource> slaveDataSources = new ConcurrentHashMap<>();
@Override
public void setEnvironment(Environment environment) {
initDefaultDataSource(environment);
initslaveDataSources(environment);
}
private void initDefaultDataSource(Environment env) {
// 讀取主資料源
Properties properties = new Properties();
EsDataSource esDataSource = new EsDataSource();
esDataSource.setUrl( env.getProperty("spring.datasource.url"));
esDataSource.setProperties(properties);
deftDataSource = esDataSource;
}
private void initslaveDataSources(Environment env) {
// 讀取組態檔獲取更多資料源
String dsPrefixs = env.getProperty("slave.datasource.names");
for (String dsPrefix : dsPrefixs.split(",")) {
// 多個資料源
Properties properties = new Properties();
EsDataSource esDataSource = new EsDataSource();
esDataSource.setUrl(env.getProperty("slave.datasource." + dsPrefix + ".url"));
esDataSource.setProperties(properties);
slaveDataSources.put(dsPrefix, esDataSource);
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
//添加默認資料源
targetDataSources.put("dataSource", this.deftDataSource);
DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
//添加其他資料源
targetDataSources.putAll(slaveDataSources);
for (String key : slaveDataSources.keySet()) {
DynamicDataSourceContextHolder.dataSourceIds.add(key);
}
//創建DynamicDataSource
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(DynamicDataSource.class);
beanDefinition.setSynthetic(true);
MutablePropertyValues mpv = beanDefinition.getPropertyValues();
mpv.addPropertyValue("defaultTargetDataSource", deftDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
//注冊 - BeanDefinitionRegistry
beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition);
logger.info("Dynamic DataSource Registry");
}
3. 自定義注解,用于攔截 mapper 執行sql 時切換資料源
import java.lang.annotation.*;
/**
* @Author zhaozhiguo
* @Date 2020-11-04
* @Description 需要切換資料源注解
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
}
4. 請求引數
/**
* @Author zhaozg
* @Date 2020-11-04
* @Description SelectParam 查詢引數
*/
public class SelectParam {
/**需要執行的SQL*/
private String sql;
/**執行SQL的資料源名稱,需要和properties slave.datasource.names 匹配*/
private String dcName;
public String getSql() {
return sql;
}
public void setSql(String sql) {
this.sql = sql;
}
public String getDcName() {
return dcName;
}
public void setDcName(String dcName) {
this.dcName = dcName;
}
}
5. AOP 監聽動態切換資料源
import com.kunlun.es.vo.SelectParam;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @Author zhaozhiguo
* @Date 2020-11-04
* @Description 動態資料源通知
*/
@Aspect
@Order(-1)
@Component
public class DynamicDattaSourceAspect {
private Logger logger = Logger.getLogger(DynamicDattaSourceAspect.class);
//改變資料源
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) {
Object[] str= joinPoint.getArgs();
SelectParam selectParams = (SelectParam) str[0];
if (!DynamicDataSourceContextHolder.isContainsDataSource(selectParams.getDcName())) {
logger.error("資料源 " + selectParams.getDcName() + " 不存在使用默認的資料源 -> " + joinPoint.getSignature());
} else {
logger.debug("使用資料源:" + selectParams.getDcName());
DynamicDataSourceContextHolder.setDataSourceType(selectParams.getDcName());
}
}
@After("@annotation(targetDataSource)")
public void clearDataSource(JoinPoint joinPoint, TargetDataSource targetDataSource) {
Object[] str= joinPoint.getArgs();
SelectParam selectParams = (SelectParam) str[0];
logger.debug("清除資料源 " + selectParams.getDcName()+ " !");
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
6. Mapper下方法 添加 TargetDataSource 注解
import com.kunlun.es.config.TargetDataSource;
import com.kunlun.es.vo.SelectParam;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
* @Author zhaozhiguo
* @Date 2020-11-04
* @Description 動態資料源通知
*/
@Mapper
public interface SelectObjMapper {
@TargetDataSource
@Select("${selectParam.sql}")
List<Map> selectObj(@Param("selectParam") SelectParam selectParam);
}
7. 啟動類,需要添加@Import(DynamicDataSourceRegister.class)
import com.kunlun.es.config.DynamicDataSourceRegister;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;
/**
* @Author zhaozhiguo
* @Date 2020-11-03
* @Description 啟動類
*/
@SpringBootApplication
@Import(DynamicDataSourceRegister.class)
public class EsSelectApplication {
public static void main(String[] args) {
SpringApplication.run(EsSelectApplication.class, args);
}
}
8. 查詢 介面暴露
import com.kunlun.es.service.SelectObjService;
import com.kunlun.es.vo.SelectParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
* @Author zhaozhiguo
* @Date 2020-11-04
* @Description 查詢介面
*/
@RestController
public class SelectObjController {
@Autowired
private SelectObjService selectObjService;
@PostMapping("/selectObj")
public List<Map> selectObj(@RequestBody SelectParam selectParam) {
return selectObjService.selectObj(selectParam);
}
}
9. 呼叫介面,大工告成!

原始碼就不上傳了,整體實作思路還是比較清楚的,jar包是淘寶花了0.56 塊大洋代下的 (此處吐槽CSDN 這個包要46積分)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/215152.html
標籤:其他
上一篇:資料在記憶體中的存盤(詳細版)
