|
@@ -1,27 +1,31 @@
|
|
|
package com.alibaba.otter.canal.client.adapter.rdb.service;
|
|
|
|
|
|
-import java.io.Reader;
|
|
|
-import java.io.StringReader;
|
|
|
-import java.math.BigDecimal;
|
|
|
-import java.nio.charset.StandardCharsets;
|
|
|
-import java.sql.*;
|
|
|
+import java.sql.Connection;
|
|
|
+import java.sql.ResultSetMetaData;
|
|
|
+import java.sql.SQLException;
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
-import java.util.concurrent.*;
|
|
|
-import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
+import java.util.concurrent.Executors;
|
|
|
+import java.util.concurrent.Future;
|
|
|
|
|
|
import javax.sql.DataSource;
|
|
|
|
|
|
-import org.joda.time.DateTime;
|
|
|
+import org.apache.commons.lang.StringUtils;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.alibaba.fastjson.serializer.SerializerFeature;
|
|
|
import com.alibaba.otter.canal.client.adapter.rdb.config.MappingConfig;
|
|
|
import com.alibaba.otter.canal.client.adapter.rdb.config.MappingConfig.DbMapping;
|
|
|
-import com.alibaba.otter.canal.client.adapter.rdb.support.SimpleDml;
|
|
|
+import com.alibaba.otter.canal.client.adapter.rdb.support.BatchExecutor;
|
|
|
+import com.alibaba.otter.canal.client.adapter.rdb.support.SingleDml;
|
|
|
import com.alibaba.otter.canal.client.adapter.rdb.support.SyncUtil;
|
|
|
+import com.alibaba.otter.canal.client.adapter.support.Dml;
|
|
|
import com.alibaba.otter.canal.client.adapter.support.Util;
|
|
|
|
|
|
/**
|
|
@@ -36,87 +40,103 @@ public class RdbSyncService {
|
|
|
|
|
|
private final Map<String, Map<String, Integer>> COLUMNS_TYPE_CACHE = new ConcurrentHashMap<>();
|
|
|
|
|
|
- private BatchExecutor[] batchExecutors;
|
|
|
+ private Map<String, Map<String, MappingConfig>> mappingConfigCache; // 库名-表名对应配置
|
|
|
|
|
|
- private int threads = 1;
|
|
|
+ private int threads = 3;
|
|
|
|
|
|
- private ExecutorService[] threadExecutors;
|
|
|
+ private List<SyncItem>[] dmlsPartition;
|
|
|
+ private BatchExecutor[] batchExecutors;
|
|
|
+ private ExecutorService[] executorThreads;
|
|
|
|
|
|
- public RdbSyncService(Integer threads, DataSource dataSource){
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
+ public RdbSyncService(Map<String, Map<String, MappingConfig>> mappingConfigCache, DataSource dataSource,
|
|
|
+ Integer threads){
|
|
|
try {
|
|
|
- if (threads != null && threads > 1 && threads <= 10) {
|
|
|
+ if (threads != null) {
|
|
|
this.threads = threads;
|
|
|
}
|
|
|
- batchExecutors = new BatchExecutor[this.threads];
|
|
|
+ this.mappingConfigCache = mappingConfigCache;
|
|
|
+ this.dmlsPartition = new List[this.threads];
|
|
|
+ this.batchExecutors = new BatchExecutor[this.threads];
|
|
|
+ this.executorThreads = new ExecutorService[this.threads];
|
|
|
for (int i = 0; i < this.threads; i++) {
|
|
|
- Connection conn = dataSource.getConnection();
|
|
|
- conn.setAutoCommit(false);
|
|
|
- this.batchExecutors[i] = new BatchExecutor(i, conn);
|
|
|
- }
|
|
|
- threadExecutors = new ExecutorService[this.threads];
|
|
|
- for (int i = 0; i < this.threads; i++) {
|
|
|
- threadExecutors[i] = Executors.newFixedThreadPool(1);
|
|
|
+ dmlsPartition[i] = new ArrayList<>();
|
|
|
+ batchExecutors[i] = new BatchExecutor(dataSource.getConnection());
|
|
|
+ executorThreads[i] = Executors.newSingleThreadExecutor();
|
|
|
}
|
|
|
} catch (SQLException e) {
|
|
|
logger.error(e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- private List<SimpleDml>[] simpleDmls2Partition(List<SimpleDml> simpleDmlList) {
|
|
|
- List<SimpleDml>[] simpleDmlPartition = new ArrayList[threads];
|
|
|
- for (int i = 0; i < threads; i++) {
|
|
|
- simpleDmlPartition[i] = new ArrayList<>();
|
|
|
- }
|
|
|
- simpleDmlList.forEach(simpleDml -> {
|
|
|
- int hash;
|
|
|
- if (simpleDml.getConfig().getConcurrent()) {
|
|
|
- hash = pkHash(simpleDml.getConfig().getDbMapping(), simpleDml.getData(), threads);
|
|
|
- } else {
|
|
|
- hash = Math.abs(Math.abs(simpleDml.getConfig().getDbMapping().getTargetTable().hashCode()) % threads);
|
|
|
- }
|
|
|
- simpleDmlPartition[hash].add(simpleDml);
|
|
|
- });
|
|
|
- return simpleDmlPartition;
|
|
|
- }
|
|
|
-
|
|
|
- public void sync(List<SimpleDml> simpleDmlList) {
|
|
|
+ public void sync(List<Dml> dmls) {
|
|
|
try {
|
|
|
- List<SimpleDml>[] simpleDmlsPartition = simpleDmls2Partition(simpleDmlList);
|
|
|
-
|
|
|
- List<Future<Boolean>> futures = new ArrayList<>();
|
|
|
- for (int i = 0; i < threads; i++) {
|
|
|
- if (!simpleDmlsPartition[i].isEmpty()) {
|
|
|
- int j = i;
|
|
|
- futures.add(threadExecutors[i].submit(() -> {
|
|
|
- simpleDmlsPartition[j].forEach(simpleDml -> sync(simpleDml, batchExecutors[j]));
|
|
|
- batchExecutors[j].commit();
|
|
|
- return true;
|
|
|
- }));
|
|
|
+ for (Dml dml : dmls) {
|
|
|
+ String destination = StringUtils.trimToEmpty(dml.getDestination());
|
|
|
+ String database = dml.getDatabase();
|
|
|
+ String table = dml.getTable();
|
|
|
+ Map<String, MappingConfig> configMap = mappingConfigCache
|
|
|
+ .get(destination + "." + database + "." + table);
|
|
|
+
|
|
|
+ for (MappingConfig config : configMap.values()) {
|
|
|
+
|
|
|
+ if (config.getConcurrent()) {
|
|
|
+ List<SingleDml> singleDmls = SingleDml.dml2SingleDmls(dml);
|
|
|
+ singleDmls.forEach(singleDml -> {
|
|
|
+ int hash = pkHash(config.getDbMapping(), singleDml.getData());
|
|
|
+ SyncItem syncItem = new SyncItem(config, singleDml);
|
|
|
+ dmlsPartition[hash].add(syncItem);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ int hash = Math.abs(Math.abs(config.getDbMapping().getTargetTable().hashCode()) % threads);
|
|
|
+ List<SingleDml> singleDmls = SingleDml.dml2SingleDmls(dml);
|
|
|
+ singleDmls.forEach(singleDml -> {
|
|
|
+ SyncItem syncItem = new SyncItem(config, singleDml);
|
|
|
+ dmlsPartition[hash].add(syncItem);
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+ List<Future> futures = new ArrayList<>();
|
|
|
+ for (int i = 0; i < threads; i++) {
|
|
|
+ int j = i;
|
|
|
+ futures.add(executorThreads[i].submit(() -> {
|
|
|
+ dmlsPartition[j].forEach(syncItem -> sync(batchExecutors[j], syncItem.config, syncItem.singleDml));
|
|
|
+ batchExecutors[j].commit();
|
|
|
+ return true;
|
|
|
+ }));
|
|
|
+ }
|
|
|
|
|
|
futures.forEach(future -> {
|
|
|
try {
|
|
|
future.get();
|
|
|
} catch (Exception e) {
|
|
|
- // ignore
|
|
|
+ logger.error(e.getMessage(), e);
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+ for (int i = 0; i < threads; i++) {
|
|
|
+ dmlsPartition[i].clear();
|
|
|
+ }
|
|
|
} catch (Exception e) {
|
|
|
- logger.error("Error rdb sync for batch", e);
|
|
|
+ logger.error(e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void sync(SimpleDml dml, BatchExecutor batchExecutor) {
|
|
|
+ private void sync(BatchExecutor batchExecutor, MappingConfig config, SingleDml dml) {
|
|
|
try {
|
|
|
- String type = dml.getType();
|
|
|
- if (type != null && type.equalsIgnoreCase("INSERT")) {
|
|
|
- insert(dml, batchExecutor);
|
|
|
- } else if (type != null && type.equalsIgnoreCase("UPDATE")) {
|
|
|
- update(dml, batchExecutor);
|
|
|
- } else if (type != null && type.equalsIgnoreCase("DELETE")) {
|
|
|
- delete(dml, batchExecutor);
|
|
|
+ if (config != null) {
|
|
|
+ String type = dml.getType();
|
|
|
+ if (type != null && type.equalsIgnoreCase("INSERT")) {
|
|
|
+ insert(batchExecutor, config, dml);
|
|
|
+ } else if (type != null && type.equalsIgnoreCase("UPDATE")) {
|
|
|
+ update(batchExecutor, config, dml);
|
|
|
+ } else if (type != null && type.equalsIgnoreCase("DELETE")) {
|
|
|
+ delete(batchExecutor, config, dml);
|
|
|
+ }
|
|
|
+ if (logger.isDebugEnabled()) {
|
|
|
+ logger.debug("DML: {}", JSON.toJSONString(dml, SerializerFeature.WriteMapNullValue));
|
|
|
+ }
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
logger.error(e.getMessage(), e);
|
|
@@ -126,53 +146,55 @@ public class RdbSyncService {
|
|
|
/**
|
|
|
* 插入操作
|
|
|
*
|
|
|
- * @param simpleDml DML数据
|
|
|
+ * @param config 配置项
|
|
|
+ * @param dml DML数据
|
|
|
*/
|
|
|
- private void insert(SimpleDml simpleDml, BatchExecutor batchExecutor) {
|
|
|
- Map<String, Object> data = simpleDml.getData();
|
|
|
+ private void insert(BatchExecutor batchExecutor, MappingConfig config, SingleDml dml) {
|
|
|
+ Map<String, Object> data = dml.getData();
|
|
|
if (data == null || data.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- DbMapping dbMapping = simpleDml.getConfig().getDbMapping();
|
|
|
-
|
|
|
- Map<String, String> columnsMap = SyncUtil.getColumnsMap(dbMapping, data);
|
|
|
+ DbMapping dbMapping = config.getDbMapping();
|
|
|
|
|
|
- StringBuilder insertSql = new StringBuilder();
|
|
|
- insertSql.append("INSERT INTO ").append(dbMapping.getTargetTable()).append(" (");
|
|
|
+ try {
|
|
|
+ Map<String, String> columnsMap = SyncUtil.getColumnsMap(dbMapping, data);
|
|
|
|
|
|
- columnsMap.forEach((targetColumnName, srcColumnName) -> insertSql.append(targetColumnName).append(","));
|
|
|
- int len = insertSql.length();
|
|
|
- insertSql.delete(len - 1, len).append(") VALUES (");
|
|
|
- int mapLen = columnsMap.size();
|
|
|
- for (int i = 0; i < mapLen; i++) {
|
|
|
- insertSql.append("?,");
|
|
|
- }
|
|
|
- len = insertSql.length();
|
|
|
- insertSql.delete(len - 1, len).append(")");
|
|
|
+ StringBuilder insertSql = new StringBuilder();
|
|
|
+ insertSql.append("INSERT INTO ").append(dbMapping.getTargetTable()).append(" (");
|
|
|
|
|
|
- Map<String, Integer> ctype = getTargetColumnType(batchExecutor.getConn(), simpleDml.getConfig());
|
|
|
+ columnsMap.forEach((targetColumnName, srcColumnName) -> insertSql.append(targetColumnName).append(","));
|
|
|
+ int len = insertSql.length();
|
|
|
+ insertSql.delete(len - 1, len).append(") VALUES (");
|
|
|
+ int mapLen = columnsMap.size();
|
|
|
+ for (int i = 0; i < mapLen; i++) {
|
|
|
+ insertSql.append("?,");
|
|
|
+ }
|
|
|
+ len = insertSql.length();
|
|
|
+ insertSql.delete(len - 1, len).append(")");
|
|
|
|
|
|
- String sql = insertSql.toString();
|
|
|
+ Map<String, Integer> ctype = getTargetColumnType(batchExecutor.getConn(), config);
|
|
|
|
|
|
- try {
|
|
|
List<Map<String, ?>> values = new ArrayList<>();
|
|
|
-
|
|
|
for (Map.Entry<String, String> entry : columnsMap.entrySet()) {
|
|
|
- String targetColumnName = entry.getKey();
|
|
|
+ String targetClolumnName = entry.getKey();
|
|
|
String srcColumnName = entry.getValue();
|
|
|
if (srcColumnName == null) {
|
|
|
- srcColumnName = targetColumnName;
|
|
|
+ srcColumnName = targetClolumnName;
|
|
|
}
|
|
|
|
|
|
- Integer type = ctype.get(targetColumnName.toLowerCase());
|
|
|
- if (type == null) {
|
|
|
- throw new RuntimeException("No column: " + targetColumnName + " found in target db");
|
|
|
- }
|
|
|
+ Integer type = ctype.get(targetClolumnName.toLowerCase());
|
|
|
+
|
|
|
Object value = data.get(srcColumnName);
|
|
|
+
|
|
|
BatchExecutor.setValue(values, type, value);
|
|
|
}
|
|
|
- batchExecutor.execute(sql, values);
|
|
|
+
|
|
|
+ batchExecutor.execute(insertSql.toString(), values);
|
|
|
+ if (logger.isTraceEnabled()) {
|
|
|
+ logger.trace("Insert into target table, sql: {}", insertSql);
|
|
|
+ }
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
|
logger.error(e.getMessage(), e);
|
|
|
}
|
|
@@ -180,30 +202,30 @@ public class RdbSyncService {
|
|
|
|
|
|
/**
|
|
|
* 更新操作
|
|
|
- *
|
|
|
- * @param simpleDml DML数据
|
|
|
+ *
|
|
|
+ * @param config 配置项
|
|
|
+ * @param dml DML数据
|
|
|
*/
|
|
|
- private void update(SimpleDml simpleDml, BatchExecutor batchExecutor) {
|
|
|
- Map<String, Object> data = simpleDml.getData();
|
|
|
+ private void update(BatchExecutor batchExecutor, MappingConfig config, SingleDml dml) {
|
|
|
+ Map<String, Object> data = dml.getData();
|
|
|
if (data == null || data.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- Map<String, Object> old = simpleDml.getOld();
|
|
|
+ Map<String, Object> old = dml.getOld();
|
|
|
if (old == null || old.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- DbMapping dbMapping = simpleDml.getConfig().getDbMapping();
|
|
|
+ DbMapping dbMapping = config.getDbMapping();
|
|
|
|
|
|
- Map<String, String> columnsMap = SyncUtil.getColumnsMap(dbMapping, data);
|
|
|
+ try {
|
|
|
+ Map<String, String> columnsMap = SyncUtil.getColumnsMap(dbMapping, data);
|
|
|
|
|
|
- Map<String, Integer> ctype = getTargetColumnType(batchExecutor.getConn(), simpleDml.getConfig());
|
|
|
+ Map<String, Integer> ctype = getTargetColumnType(batchExecutor.getConn(), config);
|
|
|
|
|
|
- try {
|
|
|
StringBuilder updateSql = new StringBuilder();
|
|
|
updateSql.append("UPDATE ").append(dbMapping.getTargetTable()).append(" SET ");
|
|
|
-
|
|
|
List<Map<String, ?>> values = new ArrayList<>();
|
|
|
for (String srcColumnName : old.keySet()) {
|
|
|
List<String> targetColumnNames = new ArrayList<>();
|
|
@@ -213,6 +235,7 @@ public class RdbSyncService {
|
|
|
}
|
|
|
});
|
|
|
if (!targetColumnNames.isEmpty()) {
|
|
|
+
|
|
|
for (String targetColumnName : targetColumnNames) {
|
|
|
updateSql.append(targetColumnName).append("=?, ");
|
|
|
Integer type = ctype.get(targetColumnName.toLowerCase());
|
|
@@ -229,7 +252,7 @@ public class RdbSyncService {
|
|
|
batchExecutor.execute(updateSql.toString(), values);
|
|
|
|
|
|
if (logger.isTraceEnabled()) {
|
|
|
- logger.trace("Execute sql: {}", updateSql);
|
|
|
+ logger.trace("Update target table, sql: {}", updateSql);
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
logger.error(e.getMessage(), e);
|
|
@@ -238,31 +261,32 @@ public class RdbSyncService {
|
|
|
|
|
|
/**
|
|
|
* 删除操作
|
|
|
- *
|
|
|
- * @param simpleDml
|
|
|
+ *
|
|
|
+ * @param config
|
|
|
+ * @param dml
|
|
|
*/
|
|
|
- private void delete(SimpleDml simpleDml, BatchExecutor batchExecutor) {
|
|
|
- Map<String, Object> data = simpleDml.getData();
|
|
|
+ private void delete(BatchExecutor batchExecutor, MappingConfig config, SingleDml dml) {
|
|
|
+ Map<String, Object> data = dml.getData();
|
|
|
if (data == null || data.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- DbMapping dbMapping = simpleDml.getConfig().getDbMapping();
|
|
|
-
|
|
|
- Map<String, Integer> ctype = getTargetColumnType(batchExecutor.getConn(), simpleDml.getConfig());
|
|
|
+ DbMapping dbMapping = config.getDbMapping();
|
|
|
|
|
|
try {
|
|
|
+ Map<String, Integer> ctype = getTargetColumnType(batchExecutor.getConn(), config);
|
|
|
+
|
|
|
StringBuilder sql = new StringBuilder();
|
|
|
sql.append("DELETE FROM ").append(dbMapping.getTargetTable()).append(" WHERE ");
|
|
|
|
|
|
List<Map<String, ?>> values = new ArrayList<>();
|
|
|
-
|
|
|
// 拼接主键
|
|
|
appendCondition(dbMapping, sql, ctype, values, data);
|
|
|
|
|
|
batchExecutor.execute(sql.toString(), values);
|
|
|
+
|
|
|
if (logger.isTraceEnabled()) {
|
|
|
- logger.trace("Execute sql: {}", sql);
|
|
|
+ logger.trace("Delete from target table, sql: {}", sql);
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
logger.error(e.getMessage(), e);
|
|
@@ -271,7 +295,7 @@ public class RdbSyncService {
|
|
|
|
|
|
/**
|
|
|
* 获取目标字段类型
|
|
|
- *
|
|
|
+ *
|
|
|
* @param conn sql connection
|
|
|
* @param config 映射配置
|
|
|
* @return 字段sqlType
|
|
@@ -305,181 +329,16 @@ public class RdbSyncService {
|
|
|
return columnType;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 设置 preparedStatement
|
|
|
- *
|
|
|
- * @param type sqlType
|
|
|
- * @param pstmt 需要设置的preparedStatement
|
|
|
- * @param value 值
|
|
|
- * @param i 索引号
|
|
|
- */
|
|
|
- public static void setPStmt(int type, PreparedStatement pstmt, Object value, int i) throws SQLException {
|
|
|
- if (value == null) {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- return;
|
|
|
- }
|
|
|
- switch (type) {
|
|
|
- case Types.BIT:
|
|
|
- case Types.BOOLEAN:
|
|
|
- if (value instanceof Boolean) {
|
|
|
- pstmt.setBoolean(i, (Boolean) value);
|
|
|
- } else if (value instanceof String) {
|
|
|
- boolean v = !value.equals("0");
|
|
|
- pstmt.setBoolean(i, v);
|
|
|
- } else if (value instanceof Number) {
|
|
|
- boolean v = ((Number) value).intValue() != 0;
|
|
|
- pstmt.setBoolean(i, v);
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.CHAR:
|
|
|
- case Types.NCHAR:
|
|
|
- case Types.VARCHAR:
|
|
|
- case Types.LONGVARCHAR:
|
|
|
- pstmt.setString(i, value.toString());
|
|
|
- break;
|
|
|
- case Types.TINYINT:
|
|
|
- if (value instanceof Number) {
|
|
|
- pstmt.setByte(i, ((Number) value).byteValue());
|
|
|
- } else if (value instanceof String) {
|
|
|
- pstmt.setByte(i, Byte.parseByte((String) value));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.SMALLINT:
|
|
|
- if (value instanceof Number) {
|
|
|
- pstmt.setShort(i, ((Number) value).shortValue());
|
|
|
- } else if (value instanceof String) {
|
|
|
- pstmt.setShort(i, Short.parseShort((String) value));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.INTEGER:
|
|
|
- if (value instanceof Number) {
|
|
|
- pstmt.setInt(i, ((Number) value).intValue());
|
|
|
- } else if (value instanceof String) {
|
|
|
- pstmt.setInt(i, Integer.parseInt((String) value));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.BIGINT:
|
|
|
- if (value instanceof Number) {
|
|
|
- pstmt.setLong(i, ((Number) value).longValue());
|
|
|
- } else if (value instanceof String) {
|
|
|
- pstmt.setLong(i, Long.parseLong((String) value));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.DECIMAL:
|
|
|
- case Types.NUMERIC:
|
|
|
- pstmt.setBigDecimal(i, new BigDecimal(value.toString()));
|
|
|
- break;
|
|
|
- case Types.REAL:
|
|
|
- if (value instanceof Number) {
|
|
|
- pstmt.setFloat(i, ((Number) value).floatValue());
|
|
|
- } else if (value instanceof String) {
|
|
|
- pstmt.setFloat(i, Float.parseFloat((String) value));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.FLOAT:
|
|
|
- case Types.DOUBLE:
|
|
|
- if (value instanceof Number) {
|
|
|
- pstmt.setDouble(i, ((Number) value).doubleValue());
|
|
|
- } else if (value instanceof String) {
|
|
|
- pstmt.setDouble(i, Double.parseDouble((String) value));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.BINARY:
|
|
|
- case Types.VARBINARY:
|
|
|
- case Types.LONGVARBINARY:
|
|
|
- case Types.BLOB:
|
|
|
-
|
|
|
- if (value instanceof byte[]) {
|
|
|
- pstmt.setBytes(i, (byte[]) value);
|
|
|
- } else if (value instanceof String) {
|
|
|
- pstmt.setBytes(i, ((String) value).getBytes(StandardCharsets.ISO_8859_1));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.CLOB:
|
|
|
- if (value instanceof byte[]) {
|
|
|
- pstmt.setBytes(i, (byte[]) value);
|
|
|
- } else if (value instanceof String) {
|
|
|
- Reader clobReader = new StringReader((String) value);
|
|
|
- pstmt.setCharacterStream(i, clobReader);
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.DATE:
|
|
|
- if (value instanceof java.util.Date) {
|
|
|
- pstmt.setDate(i, new Date(((java.util.Date) value).getTime()));
|
|
|
- } else if (value instanceof String) {
|
|
|
- String v = (String) value;
|
|
|
- if (!v.startsWith("0000-00-00")) {
|
|
|
- v = v.trim().replace(" ", "T");
|
|
|
- DateTime dt = new DateTime(v);
|
|
|
- pstmt.setDate(i, new Date(dt.toDate().getTime()));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.TIME:
|
|
|
- if (value instanceof java.util.Date) {
|
|
|
- pstmt.setTime(i, new Time(((java.util.Date) value).getTime()));
|
|
|
- } else if (value instanceof String) {
|
|
|
- String v = (String) value;
|
|
|
- v = "T" + v;
|
|
|
- DateTime dt = new DateTime(v);
|
|
|
- pstmt.setTime(i, new Time(dt.toDate().getTime()));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- case Types.TIMESTAMP:
|
|
|
- if (value instanceof java.util.Date) {
|
|
|
- pstmt.setTimestamp(i, new Timestamp(((java.util.Date) value).getTime()));
|
|
|
- } else if (value instanceof String) {
|
|
|
- String v = (String) value;
|
|
|
- if (!v.startsWith("0000-00-00")) {
|
|
|
- v = v.trim().replace(" ", "T");
|
|
|
- DateTime dt = new DateTime(v);
|
|
|
- pstmt.setTimestamp(i, new Timestamp(dt.toDate().getTime()));
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- } else {
|
|
|
- pstmt.setNull(i, type);
|
|
|
- }
|
|
|
- break;
|
|
|
- default:
|
|
|
- pstmt.setObject(i, value, type);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* 拼接主键 where条件
|
|
|
*/
|
|
|
- private static void appendCondition(DbMapping dbMapping, StringBuilder sql, Map<String, Integer> ctype,
|
|
|
- List<Map<String, ?>> values, Map<String, Object> d) {
|
|
|
+ private void appendCondition(MappingConfig.DbMapping dbMapping, StringBuilder sql, Map<String, Integer> ctype,
|
|
|
+ List<Map<String, ?>> values, Map<String, Object> d) {
|
|
|
appendCondition(dbMapping, sql, ctype, values, d, null);
|
|
|
}
|
|
|
|
|
|
- private static void appendCondition(DbMapping dbMapping, StringBuilder sql, Map<String, Integer> ctype,
|
|
|
- List<Map<String, ?>> values, Map<String, Object> d, Map<String, Object> o) {
|
|
|
+ private void appendCondition(MappingConfig.DbMapping dbMapping, StringBuilder sql, Map<String, Integer> ctype,
|
|
|
+ List<Map<String, ?>> values, Map<String, Object> d, Map<String, Object> o) {
|
|
|
// 拼接主键
|
|
|
for (Map.Entry<String, String> entry : dbMapping.getTargetPk().entrySet()) {
|
|
|
String targetColumnName = entry.getKey();
|
|
@@ -500,14 +359,25 @@ public class RdbSyncService {
|
|
|
sql.delete(len - 4, len);
|
|
|
}
|
|
|
|
|
|
+ private class SyncItem {
|
|
|
+
|
|
|
+ private MappingConfig config;
|
|
|
+ private SingleDml singleDml;
|
|
|
+
|
|
|
+ private SyncItem(MappingConfig config, SingleDml singleDml){
|
|
|
+ this.config = config;
|
|
|
+ this.singleDml = singleDml;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 取主键hash
|
|
|
*/
|
|
|
- private static int pkHash(DbMapping dbMapping, Map<String, Object> d, int threads) {
|
|
|
- return pkHash(dbMapping, d, null, threads);
|
|
|
+ private int pkHash(DbMapping dbMapping, Map<String, Object> d) {
|
|
|
+ return pkHash(dbMapping, d, null);
|
|
|
}
|
|
|
|
|
|
- private static int pkHash(DbMapping dbMapping, Map<String, Object> d, Map<String, Object> o, int threads) {
|
|
|
+ private int pkHash(DbMapping dbMapping, Map<String, Object> d, Map<String, Object> o) {
|
|
|
int hash = 0;
|
|
|
// 取主键
|
|
|
for (Map.Entry<String, String> entry : dbMapping.getTargetPk().entrySet()) {
|
|
@@ -531,11 +401,9 @@ public class RdbSyncService {
|
|
|
}
|
|
|
|
|
|
public void close() {
|
|
|
- for (BatchExecutor batchExecutor : batchExecutors) {
|
|
|
- batchExecutor.close();
|
|
|
- }
|
|
|
- for (ExecutorService executorService : threadExecutors) {
|
|
|
- executorService.shutdown();
|
|
|
+ for (int i = 0; i < threads; i++) {
|
|
|
+ batchExecutors[i].close();
|
|
|
+ executorThreads[i].shutdown();
|
|
|
}
|
|
|
}
|
|
|
}
|