mirror of
https://github.com/alibaba/canal.git
synced 2026-11-04 00:30:15 +08:00
修改外键
This commit is contained in:
@@ -58,12 +58,13 @@ public class ESSyncConfig {
|
|||||||
private String pk;
|
private String pk;
|
||||||
private String parent;
|
private String parent;
|
||||||
private String sql;
|
private String sql;
|
||||||
private List<String> skips = new ArrayList<>();
|
private List<String> skips = new ArrayList<>();
|
||||||
private boolean alwaysSql = false;
|
private int commitBatch = 1000;
|
||||||
private int commitBatch = 1000;
|
|
||||||
private String etlCondition;
|
private String etlCondition;
|
||||||
|
private boolean syncByTimestamp = false; // 是否按时间戳定时同步
|
||||||
|
private Long syncInterval; // 同步时间间隔
|
||||||
|
|
||||||
private SchemaItem schemaItem; // sql解析结果模型
|
private SchemaItem schemaItem; // sql解析结果模型
|
||||||
|
|
||||||
public String get_index() {
|
public String get_index() {
|
||||||
return _index;
|
return _index;
|
||||||
@@ -121,14 +122,6 @@ public class ESSyncConfig {
|
|||||||
this.sql = sql;
|
this.sql = sql;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAlwaysSql() {
|
|
||||||
return alwaysSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAlwaysSql(boolean alwaysSql) {
|
|
||||||
this.alwaysSql = alwaysSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCommitBatch() {
|
public int getCommitBatch() {
|
||||||
return commitBatch;
|
return commitBatch;
|
||||||
}
|
}
|
||||||
@@ -145,6 +138,22 @@ public class ESSyncConfig {
|
|||||||
this.etlCondition = etlCondition;
|
this.etlCondition = etlCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getSyncInterval() {
|
||||||
|
return syncInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSyncInterval(Long syncInterval) {
|
||||||
|
this.syncInterval = syncInterval;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSyncByTimestamp() {
|
||||||
|
return syncByTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSyncByTimestamp(boolean syncByTimestamp) {
|
||||||
|
this.syncByTimestamp = syncByTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
public SchemaItem getSchemaItem() {
|
public SchemaItem getSchemaItem() {
|
||||||
return schemaItem;
|
return schemaItem;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,267 @@
|
|||||||
|
package com.alibaba.otter.canal.client.adapter.es.service;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import org.elasticsearch.action.bulk.BulkItemResponse;
|
||||||
|
import org.elasticsearch.action.bulk.BulkRequestBuilder;
|
||||||
|
import org.elasticsearch.action.bulk.BulkResponse;
|
||||||
|
import org.elasticsearch.client.transport.TransportClient;
|
||||||
|
import org.elasticsearch.rest.RestStatus;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.alibaba.druid.pool.DruidDataSource;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.config.ESSyncConfig.ESMapping;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.config.SchemaItem.FieldItem;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.support.ESSyncUtil;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.es.support.ESTemplate;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.DatasourceConfig;
|
||||||
|
import com.alibaba.otter.canal.client.adapter.support.EtlResult;
|
||||||
|
import com.google.common.base.Joiner;
|
||||||
|
|
||||||
|
public class ESEtlService {
|
||||||
|
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(ESEtlService.class);
|
||||||
|
|
||||||
|
private TransportClient transportClient;
|
||||||
|
private ESTemplate esTemplate;
|
||||||
|
private ESSyncConfig config;
|
||||||
|
|
||||||
|
public ESEtlService(TransportClient transportClient, ESSyncConfig config){
|
||||||
|
this.transportClient = transportClient;
|
||||||
|
this.esTemplate = new ESTemplate(transportClient);
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EtlResult importData(List<String> params, boolean bulk) {
|
||||||
|
EtlResult etlResult = new EtlResult();
|
||||||
|
AtomicLong impCount = new AtomicLong();
|
||||||
|
List<String> errMsg = new ArrayList<>();
|
||||||
|
String esIndex = "";
|
||||||
|
if (config == null) {
|
||||||
|
logger.warn("esSycnCofnig is null, etl go end !");
|
||||||
|
etlResult.setErrorMessage("esSycnCofnig is null, etl go end !");
|
||||||
|
return etlResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESMapping mapping = config.getEsMapping();
|
||||||
|
|
||||||
|
esIndex = mapping.get_index();
|
||||||
|
DruidDataSource dataSource = DatasourceConfig.DATA_SOURCES.get(config.getDataSourceKey());
|
||||||
|
Pattern pattern = Pattern.compile(".*:(.*)://.*/(.*)\\?.*$");
|
||||||
|
Matcher matcher = pattern.matcher(dataSource.getUrl());
|
||||||
|
if (!matcher.find()) {
|
||||||
|
throw new RuntimeException("Not found the schema of jdbc-url: " + config.getDataSourceKey());
|
||||||
|
}
|
||||||
|
String schema = matcher.group(2);
|
||||||
|
|
||||||
|
logger.info("etl from db: {}, to es index: {}", schema, esIndex);
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
try {
|
||||||
|
String sql = mapping.getSql();
|
||||||
|
|
||||||
|
// 拼接条件
|
||||||
|
if (mapping.getEtlCondition() != null && params != null) {
|
||||||
|
String etlCondition = mapping.getEtlCondition();
|
||||||
|
int size = params.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
etlCondition = etlCondition.replace("{" + i + "}", params.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
sql += " " + etlCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("etl sql : {}", mapping.getSql());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bulk) {
|
||||||
|
// 获取总数
|
||||||
|
String countSql = "SELECT COUNT(1) FROM ( " + sql + ") _CNT ";
|
||||||
|
long cnt = (Long) ESSyncUtil.sqlRS(dataSource, countSql, rs -> {
|
||||||
|
Long count = null;
|
||||||
|
try {
|
||||||
|
if (rs.next()) {
|
||||||
|
count = ((Number) rs.getObject(1)).longValue();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return count == null ? 0L : count;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当大于1万条记录时开启多线程
|
||||||
|
if (cnt >= 10000) {
|
||||||
|
int threadCount = 3; // TODO 从配置读取默认为3
|
||||||
|
long perThreadCnt = cnt / threadCount;
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
||||||
|
List<Future<Boolean>> futures = new ArrayList<>(threadCount);
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
long offset = i * perThreadCnt;
|
||||||
|
Long size = null;
|
||||||
|
if (i != threadCount - 1) {
|
||||||
|
size = perThreadCnt;
|
||||||
|
}
|
||||||
|
String sqlFinal;
|
||||||
|
if (size != null) {
|
||||||
|
sqlFinal = sql + " LIMIT " + offset + "," + size;
|
||||||
|
} else {
|
||||||
|
sqlFinal = sql + " LIMIT " + offset + "," + cnt;
|
||||||
|
}
|
||||||
|
Future<Boolean> future = executor
|
||||||
|
.submit(() -> executeSqlImport(dataSource, sqlFinal, mapping, impCount, errMsg));
|
||||||
|
futures.add(future);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Future<Boolean> future : futures) {
|
||||||
|
future.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
executor.shutdown();
|
||||||
|
} else {
|
||||||
|
executeSqlImport(dataSource, sql, mapping, impCount, errMsg);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("自动ETL,无需统计记录总条数,直接进行ETL, index: {}", esIndex);
|
||||||
|
executeSqlImport(dataSource, sql, mapping, impCount, errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("数据全量导入完成,一共导入 {} 条数据, 耗时: {}", impCount.get(), System.currentTimeMillis() - start);
|
||||||
|
etlResult.setResultMessage("导入ES索引 " + esIndex + " 数据:" + impCount.get() + " 条");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
errMsg.add(esIndex + " etl failed! ==>" + e.getMessage());
|
||||||
|
}
|
||||||
|
if (errMsg.isEmpty()) {
|
||||||
|
etlResult.setSucceeded(true);
|
||||||
|
} else {
|
||||||
|
etlResult.setErrorMessage(Joiner.on("\n").join(errMsg));
|
||||||
|
}
|
||||||
|
return etlResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processFailBulkResponse(BulkResponse bulkResponse, boolean hasParent) {
|
||||||
|
for (BulkItemResponse response : bulkResponse.getItems()) {
|
||||||
|
if (!response.isFailed()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getFailure().getStatus() == RestStatus.NOT_FOUND) {
|
||||||
|
logger.warn(response.getFailureMessage());
|
||||||
|
} else {
|
||||||
|
logger.error("全量导入数据有误 {}", response.getFailureMessage());
|
||||||
|
throw new RuntimeException("全量数据 etl 异常: " + response.getFailureMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean executeSqlImport(DataSource ds, String sql, ESMapping mapping, AtomicLong impCount,
|
||||||
|
List<String> errMsg) {
|
||||||
|
try {
|
||||||
|
ESSyncUtil.sqlRS(ds, sql, rs -> {
|
||||||
|
int count = 0;
|
||||||
|
try {
|
||||||
|
BulkRequestBuilder bulkRequestBuilder = transportClient.prepareBulk();
|
||||||
|
|
||||||
|
long batchBegin = System.currentTimeMillis();
|
||||||
|
while (rs.next()) {
|
||||||
|
Map<String, Object> esFieldData = new LinkedHashMap<>();
|
||||||
|
for (FieldItem fieldItem : mapping.getSchemaItem().getSelectFields().values()) {
|
||||||
|
|
||||||
|
// 如果是主键字段则不插入
|
||||||
|
if (fieldItem.getFieldName().equals(mapping.get_id())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String fieldName = fieldItem.getFieldName();
|
||||||
|
if (mapping.getSkips().contains(fieldName)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object val = esTemplate.getValFromRS(mapping, rs, fieldName, fieldName);
|
||||||
|
esFieldData.put(fieldName, val);
|
||||||
|
}
|
||||||
|
Object idVal = null;
|
||||||
|
if (mapping.get_id() != null) {
|
||||||
|
idVal = rs.getObject(mapping.get_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idVal != null) {
|
||||||
|
if (mapping.getParent() == null) {
|
||||||
|
bulkRequestBuilder.add(transportClient
|
||||||
|
.prepareIndex(mapping.get_index(), mapping.get_type(), idVal.toString())
|
||||||
|
.setSource(esFieldData));
|
||||||
|
} else {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mapping.getParent() == null) {
|
||||||
|
// TODO 删除pk对应的数据
|
||||||
|
bulkRequestBuilder
|
||||||
|
.add(transportClient.prepareIndex(mapping.get_index(), mapping.get_type())
|
||||||
|
.setSource(esFieldData));
|
||||||
|
} else {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bulkRequestBuilder.numberOfActions() % mapping.getCommitBatch() == 0
|
||||||
|
&& bulkRequestBuilder.numberOfActions() > 0) {
|
||||||
|
long esBatchBegin = System.currentTimeMillis();
|
||||||
|
BulkResponse rp = bulkRequestBuilder.execute().actionGet();
|
||||||
|
if (rp.hasFailures()) {
|
||||||
|
this.processFailBulkResponse(rp, Objects.nonNull(mapping.getParent()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("全量数据批量导入批次耗时: {}, es执行时间: {}, 批次大小: {}, index; {}",
|
||||||
|
(System.currentTimeMillis() - batchBegin),
|
||||||
|
(System.currentTimeMillis() - esBatchBegin),
|
||||||
|
bulkRequestBuilder.numberOfActions(),
|
||||||
|
mapping.get_index());
|
||||||
|
}
|
||||||
|
batchBegin = System.currentTimeMillis();
|
||||||
|
bulkRequestBuilder = transportClient.prepareBulk();
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
impCount.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bulkRequestBuilder.numberOfActions() > 0) {
|
||||||
|
long esBatchBegin = System.currentTimeMillis();
|
||||||
|
BulkResponse rp = bulkRequestBuilder.execute().actionGet();
|
||||||
|
if (rp.hasFailures()) {
|
||||||
|
this.processFailBulkResponse(rp, Objects.nonNull(mapping.getParent()));
|
||||||
|
}
|
||||||
|
if (logger.isDebugEnabled()) {
|
||||||
|
logger.debug("全量数据批量导入最后批次耗时: {}, es执行时间: {}, 批次大小: {}, index; {}",
|
||||||
|
(System.currentTimeMillis() - batchBegin),
|
||||||
|
(System.currentTimeMillis() - esBatchBegin),
|
||||||
|
bulkRequestBuilder.numberOfActions(),
|
||||||
|
mapping.get_index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
errMsg.add(mapping.get_index() + " etl failed! ==>" + e.getMessage());
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error(e.getMessage(), e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -75,6 +75,11 @@ public class ESSyncService {
|
|||||||
|
|
||||||
public void sync(ESSyncConfig config, Dml dml) {
|
public void sync(ESSyncConfig config, Dml dml) {
|
||||||
try {
|
try {
|
||||||
|
// 如果是按时间戳定时更新则返回
|
||||||
|
if (config.getEsMapping().isSyncByTimestamp()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
long begin = System.currentTimeMillis();
|
long begin = System.currentTimeMillis();
|
||||||
|
|
||||||
String type = dml.getType();
|
String type = dml.getType();
|
||||||
@@ -98,6 +103,213 @@ public class ESSyncService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 插入操作dml
|
||||||
|
*
|
||||||
|
* @param config es配置
|
||||||
|
* @param dml dml数据
|
||||||
|
*/
|
||||||
|
private void insert(ESSyncConfig config, Dml dml) {
|
||||||
|
List<Map<String, Object>> dataList = dml.getData();
|
||||||
|
if (dataList == null || dataList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SchemaItem schemaItem = config.getEsMapping().getSchemaItem();
|
||||||
|
for (Map<String, Object> data : dataList) {
|
||||||
|
if (data == null || data.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schemaItem.getAliasTableItems().size() == 1 && schemaItem.isAllFieldsSimple()) {
|
||||||
|
// ------单表 & 所有字段都为简单字段------
|
||||||
|
singleTableSimpleFiledInsert(config, dml, data);
|
||||||
|
} else {
|
||||||
|
// ------是主表 查询sql来插入------
|
||||||
|
if (schemaItem.getMainTable().getTableName().equalsIgnoreCase(dml.getTable())) {
|
||||||
|
mainTableInsert(config, dml, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从表的操作
|
||||||
|
for (TableItem tableItem : schemaItem.getAliasTableItems().values()) {
|
||||||
|
if (tableItem.isMain()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!tableItem.getTableName().equals(dml.getTable())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// 关联条件出现在主表查询条件是否为简单字段
|
||||||
|
boolean allFieldsSimple = true;
|
||||||
|
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
||||||
|
if (fieldItem.isMethod() || fieldItem.isBinaryOp()) {
|
||||||
|
allFieldsSimple = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 所有查询字段均为简单字段
|
||||||
|
if (allFieldsSimple) {
|
||||||
|
// 不是子查询
|
||||||
|
if (!tableItem.isSubQuery()) {
|
||||||
|
// ------关联表简单字段插入------
|
||||||
|
Map<String, Object> esFieldData = new LinkedHashMap<>();
|
||||||
|
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
||||||
|
Object value = esTemplate.getValFromData(config.getEsMapping(),
|
||||||
|
data,
|
||||||
|
fieldItem.getFieldName(),
|
||||||
|
fieldItem.getColumn().getColumnName());
|
||||||
|
esFieldData.put(fieldItem.getFieldName(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
joinTableSimpleFieldOperation(config, dml, data, tableItem, esFieldData);
|
||||||
|
} else {
|
||||||
|
// ------关联子表简单字段插入------
|
||||||
|
subTableSimpleFieldOperation(config, dml, data, null, tableItem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ------关联子表复杂字段插入 执行全sql更新es------
|
||||||
|
wholeSqlOperation(config, dml, data, null, tableItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新操作dml
|
||||||
|
*
|
||||||
|
* @param config es配置
|
||||||
|
* @param dml dml数据
|
||||||
|
*/
|
||||||
|
private void update(ESSyncConfig config, Dml dml) {
|
||||||
|
List<Map<String, Object>> dataList = dml.getData();
|
||||||
|
List<Map<String, Object>> oldList = dml.getOld();
|
||||||
|
if (dataList == null || dataList.isEmpty() || oldList == null || oldList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SchemaItem schemaItem = config.getEsMapping().getSchemaItem();
|
||||||
|
int i = 0;
|
||||||
|
for (Map<String, Object> data : dataList) {
|
||||||
|
Map<String, Object> old = oldList.get(i);
|
||||||
|
if (data == null || data.isEmpty() || old == null || old.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schemaItem.getAliasTableItems().size() == 1 && schemaItem.isAllFieldsSimple()) {
|
||||||
|
// ------单表 & 所有字段都为简单字段------
|
||||||
|
singleTableSimpleFiledUpdate(config, dml, data, old);
|
||||||
|
} else {
|
||||||
|
// ------主表 查询sql来更新------
|
||||||
|
if (schemaItem.getMainTable().getTableName().equalsIgnoreCase(dml.getTable())) {
|
||||||
|
ESMapping mapping = config.getEsMapping();
|
||||||
|
String idFieldName = mapping.get_id() == null ? mapping.getPk() : mapping.get_id();
|
||||||
|
FieldItem idFieldItem = schemaItem.getSelectFields().get(idFieldName);
|
||||||
|
|
||||||
|
boolean idFieldSimple = true;
|
||||||
|
if (idFieldItem.isMethod() || idFieldItem.isBinaryOp()) {
|
||||||
|
idFieldSimple = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allUpdateFieldSimple = true;
|
||||||
|
out: for (FieldItem fieldItem : schemaItem.getSelectFields().values()) {
|
||||||
|
for (ColumnItem columnItem : fieldItem.getColumnItems()) {
|
||||||
|
if (old.containsKey(columnItem.getColumnName())) {
|
||||||
|
if (fieldItem.isMethod() || fieldItem.isBinaryOp()) {
|
||||||
|
allUpdateFieldSimple = false;
|
||||||
|
break out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不支持主键更新!!
|
||||||
|
|
||||||
|
// 判断是否有外键更新
|
||||||
|
boolean fkChanged = false;
|
||||||
|
for (TableItem tableItem : schemaItem.getAliasTableItems().values()) {
|
||||||
|
if (tableItem.isMain()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean changed = false;
|
||||||
|
for (List<FieldItem> fieldItems : tableItem.getRelationTableFields().values()) {
|
||||||
|
for (FieldItem fieldItem : fieldItems) {
|
||||||
|
if (old.containsKey(fieldItem.getColumn().getColumnName())) {
|
||||||
|
fkChanged = true;
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果外键有修改,则更新所对应该表的所有查询条件数据
|
||||||
|
if (changed) {
|
||||||
|
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
||||||
|
fieldItem.getColumnItems()
|
||||||
|
.forEach(columnItem -> old.put(columnItem.getColumnName(), null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断主键和所更新的字段是否全为简单字段
|
||||||
|
if (idFieldSimple && allUpdateFieldSimple && !fkChanged) {
|
||||||
|
singleTableSimpleFiledUpdate(config, dml, data, old);
|
||||||
|
} else {
|
||||||
|
mainTableUpdate(config, dml, data, old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从表的操作
|
||||||
|
for (TableItem tableItem : schemaItem.getAliasTableItems().values()) {
|
||||||
|
if (tableItem.isMain()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!tableItem.getTableName().equals(dml.getTable())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关联条件出现在主表查询条件是否为简单字段
|
||||||
|
boolean allFieldsSimple = true;
|
||||||
|
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
||||||
|
if (fieldItem.isMethod() || fieldItem.isBinaryOp()) {
|
||||||
|
allFieldsSimple = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有查询字段均为简单字段
|
||||||
|
if (allFieldsSimple) {
|
||||||
|
// 不是子查询
|
||||||
|
if (!tableItem.isSubQuery()) {
|
||||||
|
// ------关联表简单字段更新------
|
||||||
|
Map<String, Object> esFieldData = new LinkedHashMap<>();
|
||||||
|
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
||||||
|
if (old.containsKey(fieldItem.getColumn().getColumnName())) {
|
||||||
|
Object value = esTemplate.getValFromData(config.getEsMapping(),
|
||||||
|
data,
|
||||||
|
fieldItem.getFieldName(),
|
||||||
|
fieldItem.getColumn().getColumnName());
|
||||||
|
esFieldData.put(fieldItem.getFieldName(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
joinTableSimpleFieldOperation(config, dml, data, tableItem, esFieldData);
|
||||||
|
} else {
|
||||||
|
// ------关联子表简单字段更新------
|
||||||
|
subTableSimpleFieldOperation(config, dml, data, old, tableItem);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ------关联子表复杂字段更新 执行全sql更新es------
|
||||||
|
wholeSqlOperation(config, dml, data, old, tableItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除操作dml
|
||||||
|
*
|
||||||
|
* @param config es配置
|
||||||
|
* @param dml dml数据
|
||||||
|
*/
|
||||||
private void delete(ESSyncConfig config, Dml dml) {
|
private void delete(ESSyncConfig config, Dml dml) {
|
||||||
List<Map<String, Object>> dataList = dml.getData();
|
List<Map<String, Object>> dataList = dml.getData();
|
||||||
if (dataList == null || dataList.isEmpty()) {
|
if (dataList == null || dataList.isEmpty()) {
|
||||||
@@ -177,184 +389,12 @@ public class ESSyncService {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// ------关联子表复杂字段更新 执行全sql更新es------
|
// ------关联子表复杂字段更新 执行全sql更新es------
|
||||||
jonTableWholeSqlOperation(config, dml, data, null, tableItem);
|
wholeSqlOperation(config, dml, data, null, tableItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 插入操作dml
|
|
||||||
*
|
|
||||||
* @param config es配置
|
|
||||||
* @param dml dml数据
|
|
||||||
*/
|
|
||||||
private void insert(ESSyncConfig config, Dml dml) {
|
|
||||||
List<Map<String, Object>> dataList = dml.getData();
|
|
||||||
if (dataList == null || dataList.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SchemaItem schemaItem = config.getEsMapping().getSchemaItem();
|
|
||||||
for (Map<String, Object> data : dataList) {
|
|
||||||
if (data == null || data.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schemaItem.getAliasTableItems().size() == 1 && schemaItem.isAllFieldsSimple()) {
|
|
||||||
// ------单表 & 所有字段都为简单字段------
|
|
||||||
singleTableSimpleFiledInsert(config, dml, data);
|
|
||||||
} else {
|
|
||||||
// ------是主表 查询sql来插入------
|
|
||||||
if (schemaItem.getMainTable().getTableName().equalsIgnoreCase(dml.getTable())) {
|
|
||||||
mainTableInsert(config, dml, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从表的操作
|
|
||||||
for (TableItem tableItem : schemaItem.getAliasTableItems().values()) {
|
|
||||||
if (tableItem.isMain()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!tableItem.getTableName().equals(dml.getTable())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// 关联条件出现在主表查询条件是否为简单字段
|
|
||||||
boolean allFieldsSimple = true;
|
|
||||||
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
|
||||||
if (fieldItem.isMethod() || fieldItem.isBinaryOp()) {
|
|
||||||
allFieldsSimple = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 所有查询字段均为简单字段
|
|
||||||
if (allFieldsSimple) {
|
|
||||||
// 不是子查询
|
|
||||||
if (!tableItem.isSubQuery()) {
|
|
||||||
// ------关联表简单字段插入------
|
|
||||||
Map<String, Object> esFieldData = new LinkedHashMap<>();
|
|
||||||
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
|
||||||
Object value = esTemplate.getValFromData(config.getEsMapping(),
|
|
||||||
data,
|
|
||||||
fieldItem.getFieldName(),
|
|
||||||
fieldItem.getColumn().getColumnName());
|
|
||||||
esFieldData.put(fieldItem.getFieldName(), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
joinTableSimpleFieldOperation(config, dml, data, tableItem, esFieldData);
|
|
||||||
} else {
|
|
||||||
// ------关联子表简单字段插入------
|
|
||||||
subTableSimpleFieldOperation(config, dml, data, null, tableItem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// ------关联子表复杂字段插入 执行全sql更新es------
|
|
||||||
jonTableWholeSqlOperation(config, dml, data, null, tableItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新操作dml
|
|
||||||
*
|
|
||||||
* @param config es配置
|
|
||||||
* @param dml dml数据
|
|
||||||
*/
|
|
||||||
private void update(ESSyncConfig config, Dml dml) {
|
|
||||||
List<Map<String, Object>> dataList = dml.getData();
|
|
||||||
List<Map<String, Object>> oldList = dml.getOld();
|
|
||||||
if (dataList == null || dataList.isEmpty() || oldList == null || oldList.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SchemaItem schemaItem = config.getEsMapping().getSchemaItem();
|
|
||||||
int i = 0;
|
|
||||||
for (Map<String, Object> data : dataList) {
|
|
||||||
Map<String, Object> old = oldList.get(i);
|
|
||||||
if (data == null || data.isEmpty() || old == null || old.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schemaItem.getAliasTableItems().size() == 1 && schemaItem.isAllFieldsSimple()) {
|
|
||||||
// ------单表 & 所有字段都为简单字段------
|
|
||||||
singleTableSimpleFiledUpdate(config, dml, data, old);
|
|
||||||
} else {
|
|
||||||
// ------主表 查询sql来更新------
|
|
||||||
if (schemaItem.getMainTable().getTableName().equalsIgnoreCase(dml.getTable())) {
|
|
||||||
ESMapping mapping = config.getEsMapping();
|
|
||||||
String idFieldName = mapping.get_id() == null ? mapping.getPk() : mapping.get_id();
|
|
||||||
FieldItem idFieldItem = schemaItem.getSelectFields().get(idFieldName);
|
|
||||||
boolean idFieldSimple = true;
|
|
||||||
if (idFieldItem.isMethod() || idFieldItem.isBinaryOp()) {
|
|
||||||
idFieldSimple = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean allUpdateFieldSimple = true;
|
|
||||||
out: for (FieldItem fieldItem : schemaItem.getSelectFields().values()) {
|
|
||||||
for (ColumnItem columnItem : fieldItem.getColumnItems()) {
|
|
||||||
if (old.containsKey(columnItem.getColumnName())) {
|
|
||||||
if (fieldItem.isMethod() || fieldItem.isBinaryOp()) {
|
|
||||||
allUpdateFieldSimple = false;
|
|
||||||
break out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 判断主键和所更新的字段是否全为简单字段
|
|
||||||
if (idFieldSimple && allUpdateFieldSimple) {
|
|
||||||
singleTableSimpleFiledUpdate(config, dml, data, old);
|
|
||||||
} else {
|
|
||||||
mainTableUpdate(config, dml, data, old);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从表的操作
|
|
||||||
for (TableItem tableItem : schemaItem.getAliasTableItems().values()) {
|
|
||||||
if (tableItem.isMain()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!tableItem.getTableName().equals(dml.getTable())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关联条件出现在主表查询条件是否为简单字段
|
|
||||||
boolean allFieldsSimple = true;
|
|
||||||
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
|
||||||
if (fieldItem.isMethod() || fieldItem.isBinaryOp()) {
|
|
||||||
allFieldsSimple = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 所有查询字段均为简单字段
|
|
||||||
if (allFieldsSimple) {
|
|
||||||
// 不是子查询
|
|
||||||
if (!tableItem.isSubQuery()) {
|
|
||||||
// ------关联表简单字段更新------
|
|
||||||
Map<String, Object> esFieldData = new LinkedHashMap<>();
|
|
||||||
for (FieldItem fieldItem : tableItem.getRelationSelectFieldItems()) {
|
|
||||||
if (old.containsKey(fieldItem.getColumn().getColumnName())) {
|
|
||||||
Object value = esTemplate.getValFromData(config.getEsMapping(),
|
|
||||||
data,
|
|
||||||
fieldItem.getFieldName(),
|
|
||||||
fieldItem.getColumn().getColumnName());
|
|
||||||
esFieldData.put(fieldItem.getFieldName(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
joinTableSimpleFieldOperation(config, dml, data, tableItem, esFieldData);
|
|
||||||
} else {
|
|
||||||
// ------关联子表简单字段更新------
|
|
||||||
subTableSimpleFieldOperation(config, dml, data, old, tableItem);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// ------关联子表复杂字段更新 执行全sql更新es------
|
|
||||||
jonTableWholeSqlOperation(config, dml, data, old, tableItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 单表简单字段insert
|
* 单表简单字段insert
|
||||||
*
|
*
|
||||||
@@ -633,8 +673,8 @@ public class ESSyncService {
|
|||||||
* @param data 单行dml数据
|
* @param data 单行dml数据
|
||||||
* @param tableItem 当前表配置
|
* @param tableItem 当前表配置
|
||||||
*/
|
*/
|
||||||
private void jonTableWholeSqlOperation(ESSyncConfig config, Dml dml, Map<String, Object> data,
|
private void wholeSqlOperation(ESSyncConfig config, Dml dml, Map<String, Object> data, Map<String, Object> old,
|
||||||
Map<String, Object> old, TableItem tableItem) {
|
TableItem tableItem) {
|
||||||
ESMapping mapping = config.getEsMapping();
|
ESMapping mapping = config.getEsMapping();
|
||||||
StringBuilder sql = new StringBuilder(mapping.getSql() + " WHERE ");
|
StringBuilder sql = new StringBuilder(mapping.getSql() + " WHERE ");
|
||||||
|
|
||||||
|
|||||||
@@ -330,7 +330,7 @@ public class ESTemplate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (ColumnItem columnItem : fieldItem.getColumnItems()) {
|
for (ColumnItem columnItem : fieldItem.getColumnItems()) {
|
||||||
if (dmlOld.get(columnItem.getColumnName()) != null
|
if (dmlOld.containsKey(columnItem.getColumnName())
|
||||||
&& !mapping.getSkips().contains(fieldItem.getFieldName())) {
|
&& !mapping.getSkips().contains(fieldItem.getFieldName())) {
|
||||||
esFieldData.put(fieldItem.getFieldName(),
|
esFieldData.put(fieldItem.getFieldName(),
|
||||||
getValFromRS(mapping, resultSet, fieldItem.getFieldName(), fieldItem.getFieldName()));
|
getValFromRS(mapping, resultSet, fieldItem.getFieldName(), fieldItem.getFieldName()));
|
||||||
|
|||||||
@@ -10,5 +10,7 @@ esMapping:
|
|||||||
left join role b on b.id=a.role_id
|
left join role b on b.id=a.role_id
|
||||||
left join (select user_id, group_concat(label order by id desc separator ';') as labels from label
|
left join (select user_id, group_concat(label order by id desc separator ';') as labels from label
|
||||||
group by user_id) c on c.user_id=a.id"
|
group by user_id) c on c.user_id=a.id"
|
||||||
commitBatch: 3000
|
# syncByTimestamp: false
|
||||||
|
# syncInterval: 2000 # ms
|
||||||
etlCondition: "where a.c_time>='{0}'"
|
etlCondition: "where a.c_time>='{0}'"
|
||||||
|
commitBatch: 3000
|
||||||
|
|||||||
Reference in New Issue
Block a user