package com.taobao.yugong.controller; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Properties; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import javax.sql.DataSource; import org.apache.commons.configuration.Configuration; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang.StringUtils; import org.slf4j.MDC; import org.springframework.jdbc.core.JdbcTemplate; import com.google.common.collect.Lists; import com.taobao.yugong.applier.AllRecordApplier; import com.taobao.yugong.applier.CheckRecordApplier; import com.taobao.yugong.applier.FullRecordApplier; import com.taobao.yugong.applier.IncrementRecordApplier; import com.taobao.yugong.applier.MultiThreadCheckRecordApplier; import com.taobao.yugong.applier.MultiThreadFullRecordApplier; import com.taobao.yugong.applier.MultiThreadIncrementRecordApplier; import com.taobao.yugong.applier.RecordApplier; import com.taobao.yugong.common.YuGongConstants; import com.taobao.yugong.common.alarm.AlarmService; import com.taobao.yugong.common.alarm.LogAlarmService; import com.taobao.yugong.common.alarm.MailAlarmService; import com.taobao.yugong.common.db.DataSourceFactory; import com.taobao.yugong.common.db.meta.ColumnMeta; import com.taobao.yugong.common.db.meta.Table; import com.taobao.yugong.common.db.meta.TableMetaGenerator; import com.taobao.yugong.common.lifecycle.AbstractYuGongLifeCycle; import com.taobao.yugong.common.model.DataSourceConfig; import com.taobao.yugong.common.model.DbType; import com.taobao.yugong.common.model.RunMode; import com.taobao.yugong.common.model.YuGongContext; import com.taobao.yugong.common.stats.ProgressTracer; import com.taobao.yugong.common.stats.StatAggregation; import com.taobao.yugong.common.utils.LikeUtil; import com.taobao.yugong.common.utils.YuGongUtils; import com.taobao.yugong.common.utils.compile.JdkCompiler; import com.taobao.yugong.common.utils.thread.NamedThreadFactory; import com.taobao.yugong.exception.YuGongException; import com.taobao.yugong.extractor.AbstractRecordExtractor; import com.taobao.yugong.extractor.RecordExtractor; import com.taobao.yugong.extractor.oracle.AbstractOracleRecordExtractor; import com.taobao.yugong.extractor.oracle.OracleAllRecordExtractor; import com.taobao.yugong.extractor.oracle.OracleFullRecordExtractor; import com.taobao.yugong.extractor.oracle.OracleMaterializedIncRecordExtractor; import com.taobao.yugong.extractor.oracle.OracleOnceFullRecordExtractor; import com.taobao.yugong.extractor.oracle.OracleRecRecordExtractor; import com.taobao.yugong.positioner.FileMixedRecordPositioner; import com.taobao.yugong.positioner.MemoryRecordPositioner; import com.taobao.yugong.positioner.RecordPositioner; import com.taobao.yugong.translator.DataTranslator; /** * 整个迁移流程调度控制 * * @author agapple 2013-9-17 下午3:15:29 */ public class YuGongController extends AbstractYuGongLifeCycle { private DataSourceFactory dataSourceFactory = new DataSourceFactory(); private JdkCompiler compiler = new JdkCompiler(); private Configuration config; private RunMode runMode; private YuGongContext globalContext; private DbType sourceDbType = DbType.ORACLE; private DbType targetDbType = DbType.MYSQL; private File translatorDir; private AlarmService alarmService; private TableController tableController; private ProgressTracer progressTracer; private List<YuGongInstance> instances = Lists.newArrayList(); private ScheduledExecutorService schedule; // 全局的工作线程池 private ThreadPoolExecutor extractorExecutor = null; private ThreadPoolExecutor applierExecutor = null; public YuGongController(Configuration config){ this.config = config; } public void start() { MDC.remove(YuGongConstants.MDC_TABLE_SHIT_KEY); super.start(); if (!dataSourceFactory.isStart()) { dataSourceFactory.start(); } // 设置下运行模式 String mode = config.getString("yugong.table.mode"); if (StringUtils.isEmpty(mode)) { throw new YuGongException("yugong.table.mode should not be empty"); } this.runMode = RunMode.valueOf(mode); this.sourceDbType = DbType.valueOf(StringUtils.upperCase(config.getString("yugong.database.source.type"))); this.targetDbType = DbType.valueOf(StringUtils.upperCase(config.getString("yugong.database.target.type"))); this.translatorDir = new File(config.getString("yugong.translator.dir", "../conf/translator")); this.globalContext = initGlobalContext(); this.alarmService = initAlarmService(); boolean extractorDump = config.getBoolean("yugong.extractor.dump", true); boolean applierDump = config.getBoolean("yugong.applier.dump", true); int statBufferSize = config.getInt("yugong.stat.buffer.size", 16384); int statPrintInterval = config.getInt("yugong.stat.print.interval", 5); // 是否并行执行concurrent boolean concurrent = config.getBoolean("yugong.table.concurrent.enable", false); Collection<TableHolder> tableMetas = initTables(); int threadSize = 1; // 默认1,代表串行 if (concurrent) { threadSize = config.getInt("yugong.table.concurrent.size", 5); // 并行执行的table数 } tableController = new TableController(tableMetas.size(), threadSize); progressTracer = new ProgressTracer(runMode, tableMetas.size()); String alarmReceiver = config.getString("yugong.alarm.receiver", ""); int retryTimes = config.getInt("yugong.table.retry.times", 3); int retryInterval = config.getInt("yugong.table.retry.interval", 1000); int noUpdateThresoldDefault = -1; if (threadSize < tableMetas.size()) { // 如果是非一次性并发跑,默认为3次noUpdate noUpdateThresoldDefault = 3; } int noUpdateThresold = config.getInt("yugong.extractor.noupdate.thresold", noUpdateThresoldDefault); boolean useExtractorExecutor = config.getBoolean("yugong.extractor.concurrent.global", false); boolean useApplierExecutor = config.getBoolean("yugong.applier.concurrent.global", false); if (useExtractorExecutor) { int extractorSize = config.getInt("yugong.extractor.concurrent.size", 5); extractorExecutor = new ThreadPoolExecutor(extractorSize, extractorSize, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(extractorSize * 2), new NamedThreadFactory("Global-Extractor"), new ThreadPoolExecutor.CallerRunsPolicy()); } if (useApplierExecutor) { int applierSize = config.getInt("yugong.applier.concurrent.size", 5); applierExecutor = new ThreadPoolExecutor(applierSize, applierSize, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(applierSize * 2), new NamedThreadFactory("Global-Applier"), new ThreadPoolExecutor.CallerRunsPolicy()); } for (TableHolder tableHolder : tableMetas) { YuGongContext context = buildContext(globalContext, tableHolder.table, tableHolder.ignoreSchema); RecordPositioner positioner = choosePositioner(tableHolder); RecordExtractor extractor = chooseExtractor(tableHolder, context, runMode, positioner); RecordApplier applier = chooseApplier(tableHolder, context, runMode); // 可能在装载DRDS时,已经加载了一次translator处理 DataTranslator translator = tableHolder.translator; if (translator == null) { translator = choseTranslator(tableHolder); } YuGongInstance instance = new YuGongInstance(context); StatAggregation statAggregation = new StatAggregation(statBufferSize, statPrintInterval); instance.setExtractor(extractor); instance.setApplier(applier); instance.setTranslator(translator); instance.setPositioner(positioner); instance.setTableController(tableController); instance.setAlarmService(alarmService); instance.setAlarmReceiver(alarmReceiver); instance.setExtractorDump(extractorDump); instance.setApplierDump(applierDump); instance.setStatAggregation(statAggregation); instance.setRetryTimes(retryTimes); instance.setRetryInterval(retryInterval); instance.setTargetDbType(targetDbType); instance.setProgressTracer(progressTracer); instance.setNoUpdateThresold(noUpdateThresold); // 设置translator的并发数 instance.setThreadSize(config.getInt("yugong.extractor.concurrent.size", 5)); instance.setExecutor(extractorExecutor); instances.add(instance); } logger.info("## prepare start tables[{}] with concurrent[{}]", instances.size(), threadSize); int progressPrintInterval = config.getInt("yugong.progress.print.interval", 1); schedule = Executors.newScheduledThreadPool(2); schedule.scheduleWithFixedDelay(new Runnable() { @Override public void run() { try { progressTracer.print(true); } catch (Throwable e) { logger.error("print progress failed", e); } } }, progressPrintInterval, progressPrintInterval, TimeUnit.MINUTES); schedule.execute(new Runnable() { @Override public void run() { while (true) { try { YuGongInstance instance = tableController.takeDone(); if (instance.isStart()) { instance.stop(); } } catch (InterruptedException e) { // do nothging return; } catch (Throwable e) { logger.error("stop failed", e); } } } }); for (YuGongInstance instance : instances) { instance.start(); if (!concurrent) { // 如果非并发,则串行等待其返回 try { instance.waitForDone(); } catch (Exception e) { processException(instance.getContext().getTableMeta(), e); } instance.stop(); } } MDC.remove(YuGongConstants.MDC_TABLE_SHIT_KEY); } public void waitForDone() throws InterruptedException { tableController.waitForDone(); } public void stop() { super.stop(); for (YuGongInstance instance : instances) { if (instance.isStart()) { instance.stop(); } } schedule.shutdownNow(); MDC.remove(YuGongConstants.MDC_TABLE_SHIT_KEY); progressTracer.print(true); if (dataSourceFactory.isStart()) { dataSourceFactory.stop(); } MDC.remove(YuGongConstants.MDC_TABLE_SHIT_KEY); } private RecordExtractor chooseExtractor(TableHolder tableHolder, YuGongContext context, RunMode runMode, RecordPositioner positioner) { boolean once = config.getBoolean("yugong.extractor.once", false); if (sourceDbType == DbType.ORACLE) { if (runMode == RunMode.FULL || runMode == RunMode.CHECK) { String tablename = tableHolder.table.getName(); String fullName = tableHolder.table.getFullName(); // 优先找tableName String extractSql = config.getString("yugong.extractor.sql." + tablename); if (StringUtils.isEmpty(extractSql)) { extractSql = config.getString("yugong.extractor.sql." + fullName, config.getString("yugong.extractor.sql")); } // 优先找tableName String tableOnceStr = config.getString("yugong.extractor.once." + tablename); if (StringUtils.isEmpty(tableOnceStr)) { tableOnceStr = config.getString("yugong.extractor.once." + fullName); } boolean tableOnce = false; if (StringUtils.isNotEmpty(tableOnceStr)) { tableOnce = BooleanUtils.toBooleanObject(tableOnceStr).booleanValue(); } boolean forceFull = !tableOnce && StringUtils.isNotEmpty(extractSql); if (forceFull || (isOnlyPkIsNumber(tableHolder.table) && !once && !tableOnce && StringUtils.isEmpty(extractSql))) { OracleFullRecordExtractor recordExtractor = new OracleFullRecordExtractor(context); recordExtractor.setExtractSql(extractSql); recordExtractor.setTracer(progressTracer); return recordExtractor; } else { OracleOnceFullRecordExtractor recordExtractor = new OracleOnceFullRecordExtractor(context); recordExtractor.setExtractSql(extractSql); recordExtractor.setTracer(progressTracer); return recordExtractor; } } else if (runMode == RunMode.INC) { OracleMaterializedIncRecordExtractor recordExtractor = new OracleMaterializedIncRecordExtractor(context); recordExtractor.setConcurrent(config.getBoolean("yugong.extractor.concurrent.enable", true)); recordExtractor.setSleepTime(config.getLong("yugong.extractor.noupdate.sleep", 1000L)); recordExtractor.setThreadSize(config.getInt("yugong.extractor.concurrent.size", 5)); recordExtractor.setExecutor(extractorExecutor); recordExtractor.setTracer(progressTracer); return recordExtractor; } else if (runMode == RunMode.MARK || runMode == RunMode.CLEAR) { return new OracleRecRecordExtractor(context); } else { // 不会有并发问题,所以共用一份context AbstractRecordExtractor markExtractor = (AbstractRecordExtractor) chooseExtractor(tableHolder, context, RunMode.MARK, positioner); AbstractRecordExtractor fullExtractor = (AbstractRecordExtractor) chooseExtractor(tableHolder, context, RunMode.FULL, positioner); AbstractRecordExtractor incExtractor = (AbstractRecordExtractor) chooseExtractor(tableHolder, context, RunMode.INC, positioner); fullExtractor.setTracer(progressTracer); incExtractor.setTracer(progressTracer); OracleAllRecordExtractor allExtractor = new OracleAllRecordExtractor(context); allExtractor.setMarkExtractor((AbstractOracleRecordExtractor) markExtractor); allExtractor.setFullExtractor((AbstractOracleRecordExtractor) fullExtractor); allExtractor.setIncExtractor((AbstractOracleRecordExtractor) incExtractor); allExtractor.setPositioner(positioner); return allExtractor; } } else { throw new YuGongException("unsupport " + sourceDbType); } } private RecordApplier chooseApplier(TableHolder tableHolder, YuGongContext context, RunMode runMode) { boolean concurrent = config.getBoolean("yugong.applier.concurrent.enable", true); int threadSize = config.getInt("yugong.applier.concurrent.size", 5); int splitSize = context.getOnceCrawNum() / threadSize; if (splitSize > 100 || splitSize <= 0) { splitSize = 100; } if (runMode == RunMode.FULL) { if (concurrent) { return new MultiThreadFullRecordApplier(context, threadSize, splitSize, applierExecutor); } else { return new FullRecordApplier(context); } } else if (runMode == RunMode.INC) { if (concurrent) { return new MultiThreadIncrementRecordApplier(context, threadSize, splitSize, applierExecutor); } else { return new IncrementRecordApplier(context); } } else if (runMode == RunMode.ALL) { // 不会有并发问题,所以共用一份context RecordApplier fullApplier = chooseApplier(tableHolder, context, RunMode.FULL); RecordApplier incApplier = chooseApplier(tableHolder, context, RunMode.INC); AllRecordApplier allApplier = new AllRecordApplier(context); allApplier.setFullApplier(fullApplier); allApplier.setIncApplier(incApplier); return allApplier; } else if (runMode == RunMode.CHECK) { if (concurrent) { return new MultiThreadCheckRecordApplier(context, threadSize, splitSize, applierExecutor); } else { return new CheckRecordApplier(context); } } else { return new FullRecordApplier(context);// 其他情况返回一个full } } private DataTranslator choseTranslator(TableHolder tableHolder) { try { return buildTranslator(tableHolder.table.getName()); } catch (Exception e) { throw new YuGongException(e); } } private DataTranslator buildTranslator(String name) throws Exception { String tableName = YuGongUtils.toPascalCase(name); String translatorName = tableName + "DataTranslator"; String packageName = DataTranslator.class.getPackage().getName(); Class clazz = null; try { clazz = Class.forName(packageName + "." + translatorName); } catch (ClassNotFoundException e) { File file = new File(translatorDir, translatorName + ".java"); if (!file.exists()) { // 兼容下表名 file = new File(translatorDir, tableName + ".java"); if (!file.exists()) { return null; } } String javaSource = StringUtils.join(IOUtils.readLines(new FileInputStream(file)), "\n"); clazz = compiler.compile(javaSource); } return (DataTranslator) clazz.newInstance(); } private RecordPositioner choosePositioner(TableHolder tableHolder) { try { String mode = config.getString("yugong.run.positioner", "FILE"); if (StringUtils.equalsIgnoreCase("FILE", mode)) { FileMixedRecordPositioner positioner = new FileMixedRecordPositioner(); positioner.setDataDir(new File("../conf/positioner")); // 使用了../相对目录,启动脚本会确保user.dir为bin目录 positioner.setDataFileName(tableHolder.table.getSchema() + "_" + tableHolder.table.getName() + ".dat"); return positioner; } else { RecordPositioner positioner = new MemoryRecordPositioner(); return positioner; } } catch (Exception e) { throw new YuGongException(e); } } private YuGongContext buildContext(YuGongContext globalContext, Table table, boolean ignoreSchema) { YuGongContext result = globalContext.cloneGlobalContext(); result.setTableMeta(table); if (ignoreSchema) {// 自动识别table是否为无shcema定义 result.setIgnoreSchema(ignoreSchema); } return result; } private YuGongContext initGlobalContext() { YuGongContext context = new YuGongContext(); logger.info("check source database connection ..."); context.setSourceDs(initDataSource("source")); logger.info("check source database is ok"); // if (sourceDbType.isOracle() && runMode.isAll()) { // preCheckMlogGrant(context.getSourceDs()); // } logger.info("check target database connection ..."); context.setTargetDs(initDataSource("target")); logger.info("check target database is ok"); context.setSourceEncoding(config.getString("yugong.database.source.encode", "UTF-8")); context.setTargetEncoding(config.getString("yugong.database.target.encode", "UTF-8")); context.setBatchApply(config.getBoolean("yugong.table.batchApply", true)); context.setOnceCrawNum(config.getInt("yugong.table.onceCrawNum", 200)); context.setTpsLimit(config.getInt("yugong.table.tpsLimit", 2000)); context.setIgnoreSchema(config.getBoolean("yugong.table.ignoreSchema", false)); context.setSkipApplierException(config.getBoolean("yugong.table.skipApplierException", false)); context.setRunMode(runMode); return context; } private DataSource initDataSource(String type) { String username = config.getString("yugong.database." + type + ".username"); String password = config.getString("yugong.database." + type + ".password"); DbType dbType = DbType.valueOf(config.getString("yugong.database." + type + ".type")); String url = config.getString("yugong.database." + type + ".url"); String encode = config.getString("yugong.database." + type + ".encode"); String poolSize = config.getString("yugong.database." + type + ".poolSize"); Properties properties = new Properties(); if (poolSize != null) { properties.setProperty("maxActive", poolSize); } else { properties.setProperty("maxActive", "200"); } if (dbType.isMysql()) {// mysql的编码直接交给驱动去做 properties.setProperty("characterEncoding", encode); } DataSourceConfig dsConfig = new DataSourceConfig(url, username, password, dbType, properties); return dataSourceFactory.getDataSource(dsConfig); } private Collection<TableHolder> initTables() { logger.info("check source tables read privileges ..."); List tableWhiteList = config.getList("yugong.table.white"); List tableBlackList = config.getList("yugong.table.black"); boolean isEmpty = true; for (Object table : tableWhiteList) { isEmpty &= StringUtils.isBlank((String) table); } List<TableHolder> tables = Lists.newArrayList(); DbType targetDbType = YuGongUtils.judgeDbType(globalContext.getTargetDs()); if (!isEmpty) { for (Object obj : tableWhiteList) { String whiteTable = getTable((String) obj); // 先粗略判断一次 if (!isBlackTable(whiteTable, tableBlackList)) { String[] strs = StringUtils.split(whiteTable, "."); List<Table> whiteTables = null; boolean ignoreSchema = false; if (strs.length == 1) { whiteTables = TableMetaGenerator.getTableMetasWithoutColumn(globalContext.getSourceDs(), null, strs[0]); ignoreSchema = true; } else if (strs.length == 2) { whiteTables = TableMetaGenerator.getTableMetasWithoutColumn(globalContext.getSourceDs(), strs[0], strs[1]); } else { throw new YuGongException("table[" + whiteTable + "] is not valid"); } if (whiteTables.isEmpty()) { throw new YuGongException("table[" + whiteTable + "] is not found"); } for (Table table : whiteTables) { // 根据实际表名处理一下 if (!isBlackTable(table.getName(), tableBlackList) && !isBlackTable(table.getFullName(), tableBlackList)) { TableMetaGenerator.buildColumns(globalContext.getSourceDs(), table); // 构建一下拆分条件 DataTranslator translator = buildExtKeys(table, (String) obj, targetDbType); TableHolder holder = new TableHolder(table); holder.ignoreSchema = ignoreSchema; holder.translator = translator; if (!tables.contains(holder)) { tables.add(holder); } } } } } } else { List<Table> metas = TableMetaGenerator.getTableMetasWithoutColumn(globalContext.getSourceDs(), null, null); for (Table table : metas) { if (!isBlackTable(table.getName(), tableBlackList) && !isBlackTable(table.getFullName(), tableBlackList)) { TableMetaGenerator.buildColumns(globalContext.getSourceDs(), table); // 构建一下拆分条件 DataTranslator translator = buildExtKeys(table, null, targetDbType); TableHolder holder = new TableHolder(table); holder.translator = translator; if (!tables.contains(holder)) { tables.add(holder); } } } } // List<String> noPkTables = Lists.newArrayList(); // for (TableHolder tableHolder : tables) { // if (YuGongUtils.isEmpty(tableHolder.table.getPrimaryKeys())) { // noPkTables.add(tableHolder.table.getFullName()); // } // } // // if (YuGongUtils.isNotEmpty(noPkTables)) { // throw new YuGongException("Table[" + // StringUtils.join(noPkTables.toArray()) + // "] has no pks , pls check!"); // } logger.info("check source tables is ok."); return tables; } private boolean isBlackTable(String table, List tableBlackList) { for (Object tableBlack : tableBlackList) { if (LikeUtil.isMatch((String) tableBlack, table)) { return true; } } return false; } /** * 尝试构建拆分字段,如果tableStr指定了拆分字段则读取之,否则在目标库找对应的拆分字段 */ private DataTranslator buildExtKeys(Table table, String tableStr, DbType targetDbType) { DataTranslator translator = null; String extKey = getExtKey(tableStr); if (targetDbType.isDRDS()) { // 只针对目标为DRDS时处理 try { translator = buildTranslator(table.getName()); } catch (Exception e) { throw new YuGongException(e); } String schemaName = table.getSchema(); String tableName = table.getName(); if (translator != null) { // 使用源表的表名查询一次拆分表名 String tschemaName = translator.translatorSchema(); String ttableName = translator.translatorTable(); if (tschemaName != null) { schemaName = tschemaName; } if (ttableName != null) { tableName = ttableName; } } String drdsExtKey = TableMetaGenerator.getShardKeyByDRDS(globalContext.getTargetDs(), schemaName, tableName); if (extKey != null && !StringUtils.equalsIgnoreCase(drdsExtKey, extKey)) { logger.warn("table:[{}] is not matched drds shardKey:[{}]", tableStr, drdsExtKey); } extKey = drdsExtKey; } if (extKey != null) { // 以逗号切割 String[] keys = StringUtils.split(StringUtils.replace(extKey, "|", ","), ","); List<String> newExtKeys = new ArrayList<String>(); for (String key : keys) { boolean found = false; for (ColumnMeta meta : table.getPrimaryKeys()) { if (meta.getName().equalsIgnoreCase(key)) { found = true; break; } } if (!found) { // 只增加非主键的字段 newExtKeys.add(key); } } if (newExtKeys.size() > 0) { extKey = StringUtils.join(newExtKeys, ","); table.setExtKey(extKey); // 调整一下原始表结构信息,将extKeys当做主键处理 // 主要为简化extKeys变更时,等同于主键进行处理 List<ColumnMeta> primaryKeys = table.getPrimaryKeys(); List<ColumnMeta> newColumns = Lists.newArrayList(); for (ColumnMeta column : table.getColumns()) { boolean exist = false; for (String key : newExtKeys) { if (column.getName().equalsIgnoreCase(key)) { primaryKeys.add(column); exist = true; break; } } if (!exist) { newColumns.add(column); } } table.setPrimaryKeys(primaryKeys); table.setColumns(newColumns); } } return translator; } private AlarmService initAlarmService() { String emailPassword = config.getString("yugong.alarm.email.password"); if (StringUtils.isNotEmpty(emailPassword)) { MailAlarmService alarmService = new MailAlarmService(); alarmService.setEmailPassword(emailPassword); alarmService.setEmailHost(config.getString("yugong.alarm.email.host")); alarmService.setEmailUsername(config.getString("yugong.alarm.email.username")); alarmService.setStmpPort(config.getInt("yugong.alarm.email.stmp.port", 465)); alarmService.setSslSupport(config.getBoolean("yugong.alarm.email.ssl.support", true)); alarmService.start(); return alarmService; } else { return new LogAlarmService(); } } @SuppressWarnings("unused") private boolean isOnlyOnePk(Table table) { return table.getPrimaryKeys() != null && table.getPrimaryKeys().size() == 1; } private boolean isOnlyPkIsNumber(Table table) { if (table.getPrimaryKeys() != null && table.getPrimaryKeys().size() == 1) { return YuGongUtils.isNumber(table.getPrimaryKeys().get(0).getType()); } return false; } private void processException(Table table, Exception e) { MDC.remove(YuGongConstants.MDC_TABLE_SHIT_KEY); abort("process table[" + table.getFullName() + "] has error!", e); System.exit(-1);// 串行时,出错了直接退出jvm } /** * 从表白名单中得到shardKey * * @param tableName 带有shardkey的表, 例子 yugong_example_oracle#pk|name * @return */ private String getExtKey(String tableName) { if (StringUtils.isEmpty(tableName)) { return null; } String[] paramArray = tableName.split("#"); if (paramArray.length == 1) { return null; } else if (paramArray.length == 2) { return StringUtils.trim(paramArray[1]); } else { // 其他情况 return null; } } private String getTable(String tableName) { String[] paramArray = tableName.split("#"); if (paramArray.length >= 1 && !"".equals(paramArray[0])) { return paramArray[0]; } else { return null; } } private static class TableHolder { public TableHolder(Table table){ this.table = table; } Table table; boolean ignoreSchema = false; DataTranslator translator = null; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((table == null) ? 0 : table.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; TableHolder other = (TableHolder) obj; if (table == null) { if (other.table != null) return false; } else if (!table.equals(other.table)) return false; return true; } } @SuppressWarnings("unused") private void preCheckMlogGrant(DataSource ds) { JdbcTemplate jdbcTemplate = new JdbcTemplate(ds); String mlogName = "migrate" + System.nanoTime(); logger.info("check mlog privileges ..."); jdbcTemplate.execute("CREATE MATERIALIZED VIEW " + mlogName + " AS SELECT SYSDATE FROM DUAL"); jdbcTemplate.execute("DROP MATERIALIZED VIEW " + mlogName); logger.info("check mlog privileges is ok"); } }