|
@@ -38,6 +38,7 @@ import java.util.ArrayList;
|
|
|
import java.util.Collections;
|
|
|
import java.util.HashMap;
|
|
|
import java.util.HashSet;
|
|
|
+import java.util.Iterator;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Set;
|
|
@@ -121,7 +122,7 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
@Override
|
|
|
public Result reader(DatabaseConnectorMapper connectorMapper, ReaderConfig config) {
|
|
|
// 1、获取select SQL
|
|
|
- String queryKey = enableCursor() && null == config.getCursor() ? ConnectorConstant.OPERTION_QUERY_CURSOR : SqlBuilderEnum.QUERY.getName();
|
|
|
+ String queryKey = enableCursor() && null == config.getCursors() ? ConnectorConstant.OPERTION_QUERY_CURSOR : SqlBuilderEnum.QUERY.getName();
|
|
|
String querySql = config.getCommand().get(queryKey);
|
|
|
Assert.hasText(querySql, "查询语句不能为空.");
|
|
|
|
|
@@ -152,15 +153,15 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
throw new ConnectorException("writer data can not be empty.");
|
|
|
}
|
|
|
List<Field> fields = new ArrayList<>(config.getFields());
|
|
|
- Field pkField = getPrimaryKeyField(config.getFields());
|
|
|
+ List<Field> pkFields = getPrimaryKeys(config.getFields());
|
|
|
// Update / Delete
|
|
|
if (!isInsert(event)) {
|
|
|
if (isDelete(event)) {
|
|
|
fields.clear();
|
|
|
} else if (isUpdate(event)) {
|
|
|
- fields.remove(pkField);
|
|
|
+ removeFieldWithPk(fields, pkFields);
|
|
|
}
|
|
|
- fields.add(pkField);
|
|
|
+ fields.addAll(pkFields);
|
|
|
}
|
|
|
|
|
|
Result result = new Result();
|
|
@@ -170,7 +171,7 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
execute = connectorMapper.execute(databaseTemplate -> databaseTemplate.batchUpdate(executeSql, batchRows(fields, data)));
|
|
|
} catch (Exception e) {
|
|
|
logger.error(e.getMessage());
|
|
|
- data.forEach(row -> forceUpdate(result, connectorMapper, config, pkField, row));
|
|
|
+ data.forEach(row -> forceUpdate(result, connectorMapper, config, pkFields, row));
|
|
|
}
|
|
|
|
|
|
if (null != execute) {
|
|
@@ -180,7 +181,7 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
result.getSuccessData().add(data.get(i));
|
|
|
continue;
|
|
|
}
|
|
|
- forceUpdate(result, connectorMapper, config, pkField, data.get(i));
|
|
|
+ forceUpdate(result, connectorMapper, config, pkFields, data.get(i));
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
@@ -223,39 +224,41 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
|
|
|
// 获取查询数据行是否存在
|
|
|
String tableName = commandConfig.getTable().getName();
|
|
|
- String pk = PrimaryKeyUtil.findOriginalTablePrimaryKey(commandConfig.getTable());
|
|
|
- StringBuilder queryCount = new StringBuilder("SELECT COUNT(1) FROM ").append(schema).append(quotation).append(tableName).append(quotation)
|
|
|
- .append(" WHERE ").append(quotation).append(pk).append(quotation).append(" = ?");
|
|
|
+ List<String> primaryKeys = PrimaryKeyUtil.findOriginalTablePrimaryKey(commandConfig.getOriginalTable());
|
|
|
+ StringBuilder queryCount = new StringBuilder("SELECT COUNT(1) FROM ").append(schema).append(quotation).append(tableName).append(quotation).append(" WHERE ");
|
|
|
+ // id = ? AND uid = ?
|
|
|
+ PrimaryKeyUtil.buildSql(queryCount, primaryKeys, quotation, " AND ", " = ? ", true);
|
|
|
+
|
|
|
String queryCountExist = ConnectorConstant.OPERTION_QUERY_COUNT_EXIST;
|
|
|
map.put(queryCountExist, queryCount.toString());
|
|
|
return map;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 是否支持游标查询
|
|
|
+ * 查询语句表名和字段带上引号(默认不加)
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
- protected boolean enableCursor() {
|
|
|
- return false;
|
|
|
+ public String buildSqlWithQuotation() {
|
|
|
+ return "";
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 健康检查
|
|
|
+ * 是否支持游标查询
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
- protected String getValidationQuery() {
|
|
|
- return "select 1";
|
|
|
+ protected boolean enableCursor() {
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 查询语句表名和字段带上引号(默认不加)
|
|
|
+ * 健康检查
|
|
|
*
|
|
|
* @return
|
|
|
*/
|
|
|
- protected String buildSqlWithQuotation() {
|
|
|
- return "";
|
|
|
+ protected String getValidationQuery() {
|
|
|
+ return "select 1";
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -278,33 +281,6 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
return config.getSchema();
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 获取表列表
|
|
|
- *
|
|
|
- * @param connectorMapper
|
|
|
- * @param catalog
|
|
|
- * @param schema
|
|
|
- * @param tableNamePattern
|
|
|
- * @return
|
|
|
- */
|
|
|
- protected List<Table> getTable(DatabaseConnectorMapper connectorMapper, String catalog, String schema, String tableNamePattern) {
|
|
|
- return connectorMapper.execute(databaseTemplate -> {
|
|
|
- List<Table> tables = new ArrayList<>();
|
|
|
- SimpleConnection connection = (SimpleConnection) databaseTemplate.getConnection();
|
|
|
- Connection conn = connection.getConnection();
|
|
|
- String databaseCatalog = null == catalog ? conn.getCatalog() : catalog;
|
|
|
- String schemaNamePattern = null == schema ? conn.getSchema() : schema;
|
|
|
- String[] types = {TableTypeEnum.TABLE.getCode(), TableTypeEnum.VIEW.getCode(), TableTypeEnum.MATERIALIZED_VIEW.getCode()};
|
|
|
- final ResultSet rs = conn.getMetaData().getTables(databaseCatalog, schemaNamePattern, tableNamePattern, types);
|
|
|
- while (rs.next()) {
|
|
|
- final String tableName = rs.getString("TABLE_NAME");
|
|
|
- final String tableType = rs.getString("TABLE_TYPE");
|
|
|
- tables.add(new Table(tableName, tableType));
|
|
|
- }
|
|
|
- return tables;
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 获取数据库表元数据信息
|
|
|
*
|
|
@@ -366,14 +342,17 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
*/
|
|
|
protected String getQueryCountSql(CommandConfig commandConfig, String schema, String quotation, String queryFilterSql) {
|
|
|
String table = commandConfig.getTable().getName();
|
|
|
- String pk = PrimaryKeyUtil.findOriginalTablePrimaryKey(commandConfig.getTable());
|
|
|
- StringBuilder queryCount = new StringBuilder();
|
|
|
- queryCount.append("SELECT COUNT(1) FROM (SELECT 1 FROM ").append(schema).append(quotation).append(table).append(quotation);
|
|
|
+ List<String> primaryKeys = PrimaryKeyUtil.findOriginalTablePrimaryKey(commandConfig.getOriginalTable());
|
|
|
+ StringBuilder sql = new StringBuilder();
|
|
|
+ sql.append("SELECT COUNT(1) FROM (SELECT 1 FROM ").append(schema).append(quotation).append(table).append(quotation);
|
|
|
if (StringUtil.isNotBlank(queryFilterSql)) {
|
|
|
- queryCount.append(queryFilterSql);
|
|
|
+ sql.append(queryFilterSql);
|
|
|
}
|
|
|
- queryCount.append(" GROUP BY ").append(quotation).append(pk).append(quotation).append(") DBSYNCER_T");
|
|
|
- return queryCount.toString();
|
|
|
+ sql.append(" GROUP BY ");
|
|
|
+ // id,uid
|
|
|
+ PrimaryKeyUtil.buildSql(sql, primaryKeys, quotation, ",", "", true);
|
|
|
+ sql.append(") DBSYNCER_T");
|
|
|
+ return sql.toString();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -412,6 +391,33 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
return sql.toString();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取表列表
|
|
|
+ *
|
|
|
+ * @param connectorMapper
|
|
|
+ * @param catalog
|
|
|
+ * @param schema
|
|
|
+ * @param tableNamePattern
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private List<Table> getTable(DatabaseConnectorMapper connectorMapper, String catalog, String schema, String tableNamePattern) {
|
|
|
+ return connectorMapper.execute(databaseTemplate -> {
|
|
|
+ List<Table> tables = new ArrayList<>();
|
|
|
+ SimpleConnection connection = (SimpleConnection) databaseTemplate.getConnection();
|
|
|
+ Connection conn = connection.getConnection();
|
|
|
+ String databaseCatalog = null == catalog ? conn.getCatalog() : catalog;
|
|
|
+ String schemaNamePattern = null == schema ? conn.getSchema() : schema;
|
|
|
+ String[] types = {TableTypeEnum.TABLE.getCode(), TableTypeEnum.VIEW.getCode(), TableTypeEnum.MATERIALIZED_VIEW.getCode()};
|
|
|
+ final ResultSet rs = conn.getMetaData().getTables(databaseCatalog, schemaNamePattern, tableNamePattern, types);
|
|
|
+ while (rs.next()) {
|
|
|
+ final String tableName = rs.getString("TABLE_NAME");
|
|
|
+ final String tableType = rs.getString("TABLE_TYPE");
|
|
|
+ tables.add(new Table(tableName, tableType));
|
|
|
+ }
|
|
|
+ return tables;
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 根据过滤条件获取查询SQL
|
|
|
*
|
|
@@ -466,7 +472,6 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
if (CollectionUtils.isEmpty(column)) {
|
|
|
return null;
|
|
|
}
|
|
|
- String pk = null;
|
|
|
Set<String> mark = new HashSet<>();
|
|
|
List<Field> fields = new ArrayList<>();
|
|
|
for (Field c : column) {
|
|
@@ -474,9 +479,6 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
if (StringUtil.isBlank(name)) {
|
|
|
throw new ConnectorException("The field name can not be empty.");
|
|
|
}
|
|
|
- if (c.isPk()) {
|
|
|
- pk = name;
|
|
|
- }
|
|
|
if (!mark.contains(name)) {
|
|
|
fields.add(c);
|
|
|
mark.add(name);
|
|
@@ -491,11 +493,8 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
logger.error("Table name can not be empty.");
|
|
|
throw new ConnectorException("Table name can not be empty.");
|
|
|
}
|
|
|
- if (StringUtil.isBlank(pk)) {
|
|
|
- pk = PrimaryKeyUtil.findOriginalTablePrimaryKey(table);
|
|
|
- }
|
|
|
-
|
|
|
- SqlBuilderConfig config = new SqlBuilderConfig(this, schema, tableName, pk, fields, queryFilterSQL, buildSqlWithQuotation());
|
|
|
+ List<String> primaryKeys = PrimaryKeyUtil.findOriginalTablePrimaryKey(commandConfig.getOriginalTable());
|
|
|
+ SqlBuilderConfig config = new SqlBuilderConfig(this, schema, tableName, primaryKeys, fields, queryFilterSQL, buildSqlWithQuotation());
|
|
|
return SqlBuilderEnum.getSqlBuilder(type).buildSql(config);
|
|
|
}
|
|
|
|
|
@@ -552,18 +551,24 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
return args;
|
|
|
}
|
|
|
|
|
|
- private void forceUpdate(Result result, DatabaseConnectorMapper connectorMapper, WriterBatchConfig config, Field pkField,
|
|
|
+ private void forceUpdate(Result result, DatabaseConnectorMapper connectorMapper, WriterBatchConfig config, List<Field> pkFields,
|
|
|
Map row) {
|
|
|
- if(isUpdate(config.getEvent()) || isInsert(config.getEvent())){
|
|
|
+ if (isUpdate(config.getEvent()) || isInsert(config.getEvent())) {
|
|
|
// 存在执行覆盖更新,否则写入
|
|
|
final String queryCount = config.getCommand().get(ConnectorConstant.OPERTION_QUERY_COUNT_EXIST);
|
|
|
- final String event = existRow(connectorMapper, queryCount, row.get(pkField.getName())) ? ConnectorConstant.OPERTION_UPDATE : ConnectorConstant.OPERTION_INSERT;
|
|
|
+ int size = pkFields.size();
|
|
|
+ Object[] args = new Object[size];
|
|
|
+ for (int i = 0; i < size; i++) {
|
|
|
+ args[i] = row.get(pkFields.get(i).getName());
|
|
|
+ }
|
|
|
+ final String event = existRow(connectorMapper, queryCount, args) ? ConnectorConstant.OPERTION_UPDATE
|
|
|
+ : ConnectorConstant.OPERTION_INSERT;
|
|
|
logger.warn("{}表执行{}失败, 重新执行{}, {}", config.getTableName(), config.getEvent(), event, row);
|
|
|
- writer(result, connectorMapper, config, pkField, row, event);
|
|
|
+ writer(result, connectorMapper, config, pkFields, row, event);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void writer(Result result, DatabaseConnectorMapper connectorMapper, WriterBatchConfig config, Field pkField, Map row,
|
|
|
+ private void writer(Result result, DatabaseConnectorMapper connectorMapper, WriterBatchConfig config, List<Field> pkFields, Map row,
|
|
|
String event) {
|
|
|
// 1、获取 SQL
|
|
|
String sql = config.getCommand().get(event);
|
|
@@ -574,9 +579,9 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
if (isDelete(event)) {
|
|
|
fields.clear();
|
|
|
} else if (isUpdate(event)) {
|
|
|
- fields.remove(pkField);
|
|
|
+ fields.remove(pkFields);
|
|
|
}
|
|
|
- fields.add(pkField);
|
|
|
+ fields.addAll(pkFields);
|
|
|
}
|
|
|
|
|
|
try {
|
|
@@ -595,12 +600,12 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private boolean existRow(DatabaseConnectorMapper connectorMapper, String sql, Object value) {
|
|
|
+ private boolean existRow(DatabaseConnectorMapper connectorMapper, String sql, Object[] args) {
|
|
|
int rowNum = 0;
|
|
|
try {
|
|
|
- rowNum = connectorMapper.execute(databaseTemplate -> databaseTemplate.queryForObject(sql, new Object[]{value}, Integer.class));
|
|
|
+ rowNum = connectorMapper.execute(databaseTemplate -> databaseTemplate.queryForObject(sql, Integer.class, args));
|
|
|
} catch (Exception e) {
|
|
|
- logger.error("检查数据行存在异常:{},SQL:{},参数:{}", e.getMessage(), sql, value);
|
|
|
+ logger.error("检查数据行存在异常:{},SQL:{},参数:{}", e.getMessage(), sql, args);
|
|
|
}
|
|
|
return rowNum > 0;
|
|
|
}
|
|
@@ -610,4 +615,21 @@ public abstract class AbstractDatabaseConnector extends AbstractConnector implem
|
|
|
return !CollectionUtils.isEmpty(pk) && pk.contains(name);
|
|
|
}
|
|
|
|
|
|
+ private void removeFieldWithPk(List<Field> fields, List<Field> pkFields) {
|
|
|
+ if (CollectionUtils.isEmpty(fields) || CollectionUtils.isEmpty(pkFields)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ pkFields.forEach(pkField -> {
|
|
|
+ Iterator<Field> iterator = fields.iterator();
|
|
|
+ while (iterator.hasNext()) {
|
|
|
+ Field next = iterator.next();
|
|
|
+ if (next != null && StringUtil.equals(next.getName(), pkField.getName())) {
|
|
|
+ iterator.remove();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
}
|