递增值的增量字段同步

This commit is contained in:
inrgihc
2025-04-21 22:31:55 +08:00
parent a046c510ff
commit 15d55ed864
57 changed files with 952 additions and 230 deletions

View File

@@ -17,18 +17,18 @@
<div v-show="active == 1"
class="common-top">
<el-form-item label="任务名称"
label-width="100px"
label-width="160px"
:required=true
prop="name"
style="width:65%">
<el-input v-model="dataform.name"
auto-complete="off"
placeholder="请输入任务名称"
style="width:50%"></el-input>
style="width:80%"></el-input>
<label class="tips-style block">请输入任务名称只能以字母数字为开头包含字母数字和._-3-100个字符</label>
</el-form-item>
<el-form-item label="描述"
label-width="100px"
label-width="160px"
prop="description"
style="width:65%">
<el-input v-model="dataform.description"
@@ -36,13 +36,13 @@
:rows="3"
auto-complete="off"
placeholder="请输入任务描述"
style="width:50%"></el-input>
style="width:80%"></el-input>
</el-form-item>
<el-form-item label="集成模式"
label-width="100px"
label-width="160px"
:required=true
prop="scheduleMode"
style="width:65%">
style="width:80%">
<el-input v-model="dataform.scheduleMode"
v-if="false"></el-input>
<el-radio-group v-model="dataform.scheduleModeName"
@@ -55,8 +55,8 @@
</el-radio-group>
</el-form-item>
<el-form-item label="执行周期"
label-width="100px"
style="width:65%"
label-width="160px"
style="width:80%"
:required="true"
v-if="dataform.scheduleMode=='SYSTEM_SCHEDULED'">
<el-select v-model="dataform.cronExpression"
@@ -73,10 +73,10 @@
<div v-show="active == 2"
class="common-top">
<el-form-item label="源端数据源"
label-width="100px"
label-width="160px"
:required=true
prop="sourceConnectionId"
style="width:65%">
style="width:80%">
<el-select v-model="dataform.sourceConnectionId"
@change="selectChangedSourceConnection"
placeholder="请选择">
@@ -87,10 +87,10 @@
</el-select>
</el-form-item>
<el-form-item label="源端模式名"
label-width="100px"
label-width="160px"
:required=true
prop="sourceSchema"
style="width:65%">
style="width:80%">
<el-select v-model="dataform.sourceSchema"
filterable
@change="selectCreateChangedSourceSchema"
@@ -102,10 +102,10 @@
</el-select>
</el-form-item>
<el-form-item label="源端表类型"
label-width="100px"
label-width="160px"
:required=true
prop="tableType"
style="width:65%">
style="width:80%">
<el-select placeholder="请选择表类型"
@change="selectCreateChangedTableType"
v-model="dataform.tableType">
@@ -116,10 +116,10 @@
</el-select>
</el-form-item>
<el-form-item label="配置方式"
label-width="100px"
label-width="160px"
:required=true
prop="includeOrExclude"
style="width:65%">
style="width:80%">
<el-select placeholder="请选择配置方式"
v-model="dataform.includeOrExclude">
<el-option label="包含表"
@@ -129,10 +129,10 @@
</el-select>
</el-form-item>
<el-form-item label="表名配置"
label-width="100px"
label-width="160px"
:required=false
prop="sourceTables"
style="width:65%">
style="width:80%">
<el-select placeholder="请选择表名"
multiple
filterable
@@ -142,16 +142,70 @@
:label="item"
:value="item"></el-option>
</el-select>
<label class="tips-style block">当为包含表时选择所要精确包含的表名如果不选则代表选择所有当为排除表时选择要精确排除的表名</label>
<label class="tips-style block">当为包含表时选择所要精确包含的表名如果不选则代表选择所有当为排除表时必须选择要精确排除的表名</label>
</el-form-item>
<el-form-item label="增量同步配置"
label-width="160px"
:required=false
style="width:80%">
&nbsp;&nbsp;
<i class="el-icon-plus"
@click="handleAddInputIncrTable"></i>
&nbsp;&nbsp;&nbsp;&nbsp;
<i class="el-icon-question"
@click="showDataSyncMessageDialogVisible=true"></i>
<el-table :data="dataform.incrTableColumns"
:header-cell-style="{background:'#eef1f6',color:'#606266'}"
size="mini"
border>
<el-table-column label="表名"
prop="tableName"
min-width="45%">
</el-table-column>
<el-table-column label="增量字段名"
prop="columnName"
min-width="45%">
</el-table-column>
<el-table-column label="操作"
min-width="10%">
<template slot-scope="scope">
<el-link icon="el-icon-delete"
@click="handleDeleteIncrTable(scope.$index)"></el-link>
</template>
</el-table-column>
</el-table>
<label class="tips-style block">可点击加号+按钮为需要增量同步的大表配置增量同步的字段来加快数据同步速度</label>
</el-form-item>
<el-form-item label="同步前置执行SQL脚本"
label-width="160px"
prop="sourceBeforeSqlScripts"
style="width:80%">
<el-input v-model="dataform.sourceBeforeSqlScripts"
type="textarea"
:rows="3"
auto-complete="off"
style="width: 80%"></el-input>
<label class="tips-style block">数据同步查询源端数据库前执行的SQL多个SQL间以英文逗号分隔</label>
</el-form-item>
<el-form-item label="同步后置执行SQL脚本"
label-width="160px"
prop="sourceAfterSqlScripts"
style="width:80%">
<el-input v-model="dataform.sourceAfterSqlScripts"
type="textarea"
:rows="3"
auto-complete="off"
style="width: 80%"></el-input>
<label class="tips-style block">数据同步查询源端数据库后执行的SQL多个SQL间以英文逗号分隔</label>
</el-form-item>
</div>
<div v-show="active == 3"
class="common-top">
<el-form-item label="目的端数据源"
label-width="120px"
label-width="160px"
:required=true
prop="targetConnectionId"
style="width:65%">
style="width:80%">
<el-select v-model="dataform.targetConnectionId"
@change="selectChangedTargetConnection"
placeholder="请选择">
@@ -162,10 +216,10 @@
</el-select>
</el-form-item>
<el-form-item label="目的端模式名"
label-width="120px"
label-width="160px"
:required=true
prop="targetSchema"
style="width:65%">
style="width:80%">
<el-select v-model="dataform.targetSchema"
filterable
placeholder="请选择">
@@ -176,10 +230,10 @@
</el-select>
</el-form-item>
<el-form-item label="自动同步模式"
label-width="120px"
label-width="160px"
:required=true
prop="autoSyncMode"
style="width:65%">
style="width:80%">
<span slot="label">
<span style="color: red"><strong>自动同步模式</strong> </span>
</span>
@@ -202,11 +256,11 @@
</el-select>
</el-form-item>
<el-form-item label="建表字段自增"
label-width="120px"
label-width="160px"
:required=true
v-if=" dataform.autoSyncMode!==0 "
prop="targetAutoIncrement"
style="width:65%">
style="width:80%">
<el-tooltip placement="top">
<div slot="content">
创建表时是否自动支持字段的自增只有使用自动建表才会生效不过前提需要两端的数据库表建表时支持指定自增字段默认为false
@@ -221,10 +275,10 @@
</el-select>
</el-form-item>
<el-form-item label="表名转换方法"
label-width="130px"
label-width="160px"
:required=true
prop="tableNameCase"
style="width:45%">
style="width:80%">
<el-tooltip placement="top">
<div slot="content">
转换说明先使用下面的表名映射然后再使用这里的表名转换方法转换对支持大小写敏感的数据库类型有效
@@ -239,10 +293,10 @@
</el-select>
</el-form-item>
<el-form-item label="列名转换方法"
label-width="130px"
label-width="160px"
:required=true
prop="columnNameCase"
style="width:45%">
style="width:80%">
<el-tooltip placement="top">
<div slot="content">
转换说明先使用下面的列名映射然后再使用这里的转换方法转换对支持大小写敏感的数据库类型有效
@@ -257,11 +311,11 @@
</el-select>
</el-form-item>
<el-form-item label="数据批次大小"
label-width="120px"
label-width="160px"
:required=true
v-if=" dataform.autoSyncMode!==1 "
prop="batchSize"
style="width:65%">
style="width:80%">
<el-tooltip placement="top">
<div slot="content">
数据同步时单个批次处理的行记录总数该值越大越占用内存空间建议小字段表设置为10000或20000大字段表设置为100或500
@@ -276,11 +330,11 @@
</el-select>
</el-form-item>
<el-form-item label="通道队列大小"
label-width="120px"
label-width="160px"
:required=true
v-if=" dataform.autoSyncMode!==1 "
prop="channelSize"
style="width:65%">
style="width:80%">
<el-tooltip placement="top">
<div slot="content">
数据同步时缓存数据的通道队列大小该值越大越占用内存空间当源库读取快目标库写入慢时缓存在内存中的数据最大占用空间 = 行记录大小 × 数据批次大小 × 通道队列大小
@@ -295,11 +349,11 @@
</el-select>
</el-form-item>
<el-form-item label="同步操作方法"
label-width="120px"
label-width="160px"
:required=true
v-if=" dataform.autoSyncMode!==1 "
prop="targetSyncOption"
style="width:65%">
style="width:80%">
<el-tooltip placement="top">
<div slot="content">
<p>数据同步时包括增删改操作这里选择配置执行INSERTUPDATEDELETE操作类型的方法;</p>
@@ -318,26 +372,26 @@
<el-form-item label="同步前置执行SQL脚本"
label-width="160px"
v-if=" dataform.autoSyncMode!==1 "
prop="beforeSqlScripts"
style="width:65%">
<el-input v-model="dataform.beforeSqlScripts"
prop="targetBeforeSqlScripts"
style="width:80%">
<el-input v-model="dataform.targetBeforeSqlScripts"
type="textarea"
:rows="3"
auto-complete="off"
style="width: 65%"></el-input>
style="width: 80%"></el-input>
<label class="tips-style block">数据同步写入目标端数据库前执行的SQL多个SQL间以英文逗号分隔使用场景如MySQL数据库关闭外键约束 SET FOREIGN_KEY_CHECKS
= 0</label>
</el-form-item>
<el-form-item label="同步后置执行SQL脚本"
label-width="160px"
v-if=" dataform.autoSyncMode!==1 "
prop="afterSqlScripts"
style="width:65%">
<el-input v-model="dataform.afterSqlScripts"
prop="targetAfterSqlScripts"
style="width:80%">
<el-input v-model="dataform.targetAfterSqlScripts"
type="textarea"
:rows="3"
auto-complete="off"
style="width: 65%"></el-input>
style="width: 80%"></el-input>
<label class="tips-style block">数据同步写入目标端数据库后执行的SQL多个SQL间以英文逗号分隔使用场景如MySQL数据库恢复外键约束 SET FOREIGN_KEY_CHECKS = 1</label>
</el-form-item>
</div>
@@ -448,6 +502,93 @@
提交
</el-button>
<el-dialog v-if="active == 2"
title="选择增量同步表的增量标识字段"
:visible.sync="columnNameIncrementDialogVisible"
:showClose="false"
:before-close="handleClose">
<el-select @change="queryPreviewColumnNameMapperList"
v-model="preiveTableName"
placeholder="请选择">
<el-option v-for="(item,index) in preiveSeeTableNameList"
:key="index"
:label="item"
:value="item"></el-option>
</el-select>
<el-table :header-cell-style="{background:'#eef1f6',color:'#606266'}"
:data="columnNamesMapperData"
@row-click="singleRowClick"
highlight-current-row
size="mini"
border>
<el-table-column label="#"
min-width="10%">
<template slot-scope="scope">
{{scope.$index}}
</template>
</el-table-column>
<el-table-column prop="originalName"
label="字段名"
min-width="30%"></el-table-column>
<el-table-column prop="typeName"
label="字段类型"
min-width="30%"></el-table-column>
<el-table-column prop="canIncrement"
label="可标识增量"
min-width="20%">
<template slot-scope="scope">
<el-tag size="medium">{{ boolValueFormat(scope.row.canIncrement) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="选择"
min-width="10%">
<template slot-scope="scope">
<el-radio :label="scope.row.originalName"
v-model="radio"
:disabled="!scope.row.canIncrement"
@change.native="singleRowClick(scope.row)">{{""}}</el-radio>
</template>
</el-table-column>
</el-table>
<div slot="footer"
class="dialog-footer">
<el-button @click="handleConfirmSelectIncrTableColumn">确定</el-button>
<el-button @click="handleCancelSelectIncrTableColumn">取消</el-button>
</div>
</el-dialog>
<el-dialog v-if="active == 2"
title="提示信息"
:visible.sync="showDataSyncMessageDialogVisible"
:showClose="false"
:before-close="handleClose">
<el-alert title="1、数据同步概念"
type="warning"
:closable="false"
show-icon>
<ul>
<li><b>全量同步:</b> 先truncate清空目标表后然后将源端表数据全部插入目标表的过程</li>
<li><b>增量同步:</b> 根据增量表指定的增量字段使用带有WHERE field > value的条件SQL查询源端表数据然后插入目标表的过程</li>
<li><b>变化量同步:</b> 在源端表和目标表都有主键且映射一致的条件下通过两边数据比对计算出差异然后目标表执行插入/更新/删除数据的过程</li>
</ul>
</el-alert>
<el-alert title="2、dbswitch同步逻辑"
type="info"
:closable="false"
show-icon>
<ul>
<li><b>步骤1:</b> 如果是首次同步则会自动创建目标表并执行全量数据同步;</li>
<li><b>步骤2:</b> 非首次同步时如果表配置了增量同步标识字段则会执行增量数据同步;</li>
<li><b>步骤3:</b> 非首次同步时且没有配置增量同步标识字段如果两端都有主键且映射一致则会执行变化量数据同步;</li>
<li><b>步骤3:</b> 非首次同步时且没有配置增量同步标识字段如果两端没有主键或主键不一致则会执行全量数据同步;</li>
</ul>
</el-alert>
<div slot="footer"
class="dialog-footer">
<el-button @click="showDataSyncMessageDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<el-dialog v-if="active == 4"
title="查看表名映射关系"
:visible.sync="tableNameMapperDialogVisible"
@@ -603,6 +744,9 @@ export default {
tableType: "TABLE",
includeOrExclude: "",
sourceTables: [],
incrTableColumns: [],
sourceBeforeSqlScripts: "",
sourceAfterSqlScripts: "",
tableNameMapper: [],
columnNameMapper: [],
tableNameCase: 'NONE',
@@ -617,8 +761,8 @@ export default {
batchSize: 5000,
channelSize: 100,
targetSyncOption: 'INSERT_UPDATE_DELETE',
beforeSqlScripts: '',
afterSqlScripts: '',
targetBeforeSqlScripts: '',
targetAfterSqlScripts: '',
},
rules: {
name: [
@@ -718,17 +862,22 @@ export default {
]
},
active: 1,
radio: '0',
sourceConnection: {},
targetConnection: {},
sourceConnectionSchemas: [],
sourceSchemaTables: [],
targetConnectionSchemas: [],
columnNameIncrementDialogVisible: false,
showDataSyncMessageDialogVisible: false,
tableNameMapperDialogVisible: false,
columnNameMapperDialogVisible: false,
tableNamesMapperData: [],
columnNamesMapperData: [],
preiveSeeTableNameList: [],
preiveTableName: "",
tempIncrTableName: "",
tempIncrColumnName: "",
}
},
methods: {
@@ -748,6 +897,13 @@ export default {
this.dataform.scheduleMode = "MANUAL"
}
},
boolValueFormat (value) {
if (value === true) {
return "是";
} else {
return "否";
}
},
handleClose (done) {
},
next () {
@@ -803,6 +959,9 @@ export default {
tableType: detail.configuration.tableType,
includeOrExclude: detail.configuration.includeOrExclude,
sourceTables: detail.configuration.sourceTables,
incrTableColumns: detail.configuration.incrTableColumns,
sourceBeforeSqlScripts: detail.configuration.sourceBeforeSqlScripts,
sourceAfterSqlScripts: detail.configuration.sourceAfterSqlScripts,
tableNameMapper: detail.configuration.tableNameMapper,
columnNameMapper: detail.configuration.columnNameMapper,
tableNameCase: detail.configuration.tableNameCase,
@@ -818,9 +977,9 @@ export default {
batchSize: detail.configuration.batchSize,
channelSize: detail.configuration.channelSize,
targetSyncOption: detail.configuration.targetSyncOption,
beforeSqlScripts: detail.configuration.beforeSqlScripts,
afterSqlScripts: detail.configuration.afterSqlScripts,
}
targetBeforeSqlScripts: detail.configuration.targetBeforeSqlScripts,
targetAfterSqlScripts: detail.configuration.targetAfterSqlScripts,
};
this.selectChangedSourceConnection(this.dataform.sourceConnectionId)
this.selectCreateChangedSourceSchema(this.dataform.sourceSchema)
this.selectChangedTargetConnection(this.dataform.targetConnectionId)
@@ -908,6 +1067,47 @@ export default {
});
}
},
handleAddInputIncrTable: function () {
if (!this.dataform.sourceConnectionId || this.dataform.sourceConnectionId < 0
|| !this.dataform.sourceSchema || this.dataform.sourceSchema.length == 0) {
alert("请选择【源端数据源】和【源端模式名】!");
return;
}
if (!this.dataform.includeOrExclude) {
alert("请选择源端表选择的【配置方式】!");
return;
}
if (this.dataform.includeOrExclude == "INCLUDE") {
if (this.dataform.sourceTables.length == 0) {
this.preiveSeeTableNameList = this.sourceSchemaTables;
} else {
this.preiveSeeTableNameList = this.dataform.sourceTables;
}
} else {
if (this.dataform.sourceTables.length == 0) {
alert("请选择排除表的【表名配置】!");
return;
}
// 排除表,求差集
this.preiveSeeTableNameList = JSON.parse(JSON.stringify(this.sourceSchemaTables));
for (var i = 0; i < this.dataform.sourceTables.length; ++i) {
var one = this.dataform.sourceTables[i];
this.preiveSeeTableNameList.some((item, index) => {
if (item == one) {
this.preiveSeeTableNameList.splice(index, 1)
return true;
}
})
}
}
this.columnNameIncrementDialogVisible = true;
},
handleDeleteIncrTable: function (index) {
this.dataform.incrTableColumns.splice(index, 1);
},
selectChangedTargetConnection: function (value) {
this.targetConnection = this.connectionNameList.find(
(item) => {
@@ -942,6 +1142,11 @@ export default {
return;
}
if (!this.dataform.includeOrExclude) {
alert("请选择源端表选择的【配置方式】!");
return;
}
this.$http({
method: "POST",
headers: {
@@ -1051,7 +1256,56 @@ export default {
}
}
});
},
singleRowClick (row) {
if (row.canIncrement) {
this.tempIncrTableName = this.preiveTableName;
this.tempIncrColumnName = row.originalName;
this.radio = row.originalName;
console.log("table=" + this.tempIncrTableName + ";column=" + this.tempIncrColumnName)
} else {
this.$alert("非整型或日期时间类型不能被选中", "提示信息",
{
confirmButtonText: "确定",
type: "warn"
}
);
}
},
handleConfirmSelectIncrTableColumn: function () {
if (!this.tempIncrTableName || !this.tempIncrColumnName) {
this.$alert("请选择一个标识增量字段来", "错误信息",
{
confirmButtonText: "确定",
type: "error"
}
);
return;
}
if (!this.dataform.incrTableColumns.find(item => item.tableName === this.tempIncrTableName)) {
this.dataform.incrTableColumns.push(
{
tableName: this.tempIncrTableName,
columnName: this.tempIncrColumnName
}
);
this.handleCancelSelectIncrTableColumn();
} else {
this.$alert("已经存在增量同步表[" + this.tempIncrTableName + "]的配置了", "提示信息",
{
confirmButtonText: "确定",
type: "info"
}
);
}
},
handleCancelSelectIncrTableColumn: function () {
this.columnNameIncrementDialogVisible = false;
this.preiveTableName = "";
this.columnNamesMapperData = [];
this.tempIncrTableName = "";
this.tempIncrColumnName = "";
this.radio = "";
},
handleSave: function () {
if (0 === this.dataform.autoSyncMode) {
@@ -1085,6 +1339,9 @@ export default {
tableType: this.dataform.tableType,
includeOrExclude: this.dataform.includeOrExclude,
sourceTables: this.dataform.sourceTables,
incrTableColumns: this.dataform.incrTableColumns,
sourceBeforeSqlScripts: this.dataform.sourceBeforeSqlScripts,
sourceAfterSqlScripts: this.dataform.sourceAfterSqlScripts,
targetConnectionId: this.dataform.targetConnectionId,
targetSchema: this.dataform.targetSchema,
tableNameMapper: this.dataform.tableNameMapper,
@@ -1097,8 +1354,8 @@ export default {
batchSize: this.dataform.batchSize,
channelSize: this.dataform.channelSize,
targetSyncOption: this.dataform.targetSyncOption,
beforeSqlScripts: this.dataform.beforeSqlScripts,
afterSqlScripts: this.dataform.afterSqlScripts,
targetBeforeSqlScripts: this.dataform.targetBeforeSqlScripts,
targetAfterSqlScripts: this.dataform.targetAfterSqlScripts,
}
})
}).then(res => {
@@ -1132,6 +1389,9 @@ export default {
tableType: this.dataform.tableType,
includeOrExclude: this.dataform.includeOrExclude,
sourceTables: this.dataform.sourceTables,
incrTableColumns: this.dataform.incrTableColumns,
sourceBeforeSqlScripts: this.dataform.sourceBeforeSqlScripts,
sourceAfterSqlScripts: this.dataform.sourceAfterSqlScripts,
targetConnectionId: this.dataform.targetConnectionId,
targetSchema: this.dataform.targetSchema,
tableNameMapper: this.dataform.tableNameMapper,
@@ -1144,8 +1404,8 @@ export default {
batchSize: this.dataform.batchSize,
channelSize: this.dataform.channelSize,
targetSyncOption: this.dataform.targetSyncOption,
beforeSqlScripts: this.dataform.beforeSqlScripts,
afterSqlScripts: this.dataform.afterSqlScripts,
targetBeforeSqlScripts: this.dataform.targetBeforeSqlScripts,
targetAfterSqlScripts: this.dataform.targetAfterSqlScripts,
}
})
}).then(res => {

View File

@@ -108,6 +108,49 @@
</div>
</el-col>
</el-row>
<el-row class="row-gutter">
<el-col :span="4">
<label class="key-text">增量同步配置</label>
</el-col>
<el-col :span="20">
<el-table :data="infoform.incrTableColumns"
style="width: 100%"
:row-class-name="tableRowClassName">
<el-table-column prop="tableName"
label="增量同步表名">
</el-table-column>
<el-table-column prop="columnName"
label="增量标识字段">
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-row class="row-gutter">
<el-col :span="8">
<label class="key-text">同步前置执行SQL脚本</label>
</el-col>
<el-col :span="16">
<label class="value-text">
<span v-show="!infoform.sourceBeforeSqlScripts || infoform.sourceBeforeSqlScripts.length==0">[SQL脚本内容为空]</span>
<span v-show="infoform.sourceBeforeSqlScripts && infoform.sourceBeforeSqlScripts.length>0">{{
infoform.sourceBeforeSqlScripts
}}</span>
</label>
</el-col>
</el-row>
<el-row class="row-gutter">
<el-col :span="8">
<label class="key-text">同步后置执行SQL脚本</label>
</el-col>
<el-col :span="16">
<label class="value-text">
<span v-show="!infoform.sourceAfterSqlScripts || infoform.sourceAfterSqlScripts.length==0">[SQL脚本内容为空]</span>
<span v-show="infoform.sourceAfterSqlScripts && infoform.sourceAfterSqlScripts.length>0">{{
infoform.sourceAfterSqlScripts
}}</span>
</label>
</el-col>
</el-row>
</div>
</div>
<div class="target">
@@ -246,9 +289,9 @@
<el-col :span="16">
<label class="value-text"
v-if=" infoform.autoSyncMode!==1 ">
<span v-show="!infoform.beforeSqlScripts || infoform.beforeSqlScripts.length==0">[SQL脚本内容为空]</span>
<span v-show="infoform.beforeSqlScripts && infoform.beforeSqlScripts.length>0">{{
infoform.beforeSqlScripts
<span v-show="!infoform.targetBeforeSqlScripts || infoform.targetBeforeSqlScripts.length==0">[SQL脚本内容为空]</span>
<span v-show="infoform.targetBeforeSqlScripts && infoform.targetBeforeSqlScripts.length>0">{{
infoform.targetBeforeSqlScripts
}}</span>
</label>
</el-col>
@@ -260,9 +303,9 @@
<el-col :span="16">
<label class="value-text"
v-if=" infoform.autoSyncMode!==1 ">
<span v-show="!infoform.afterSqlScripts || infoform.afterSqlScripts.length==0">[SQL脚本内容为空]</span>
<span v-show="infoform.afterSqlScripts && infoform.afterSqlScripts.length>0">{{
infoform.afterSqlScripts
<span v-show="!infoform.targetAfterSqlScripts || infoform.targetAfterSqlScripts.length==0">[SQL脚本内容为空]</span>
<span v-show="infoform.targetAfterSqlScripts && infoform.targetAfterSqlScripts.length>0">{{
infoform.targetAfterSqlScripts
}}</span>
</label>
</el-col>
@@ -325,6 +368,9 @@ export default {
tableType: "TABLE",
includeOrExclude: "",
sourceTables: [],
incrTableColumns: [],
sourceBeforeSqlScripts: "",
sourceAfterSqlScripts: "",
tableNameMapper: [],
columnNameMapper: [],
tableNameCase: 'NONE',
@@ -338,8 +384,8 @@ export default {
batchSize: 5000,
channelSize: 100,
targetSyncOption: 'INSERT_UPDATE_DELETE',
beforeSqlScripts: '',
afterSqlScripts: '',
targetBeforeSqlScripts: '',
targetAfterSqlScripts: '',
}
}
},
@@ -462,4 +508,8 @@ export default {
padding: 2px 2px;
border-bottom: 1px solid #e0e0e0;
}
/deep/.el-table .el-table__cell {
padding: 0px;
}
</style>

View File

@@ -79,6 +79,9 @@ export default {
tableType: detail.configuration.tableType,
includeOrExclude: detail.configuration.includeOrExclude,
sourceTables: detail.configuration.sourceTables,
incrTableColumns: detail.configuration.incrTableColumns,
sourceBeforeSqlScripts: detail.configuration.sourceBeforeSqlScripts,
sourceAfterSqlScripts: detail.configuration.sourceAfterSqlScripts,
tableNameMapper: detail.configuration.tableNameMapper,
columnNameMapper: detail.configuration.columnNameMapper,
tableNameCase: detail.configuration.tableNameCase,
@@ -94,8 +97,8 @@ export default {
batchSize: detail.configuration.batchSize,
channelSize: detail.configuration.channelSize,
targetSyncOption: detail.configuration.targetSyncOption,
beforeSqlScripts: detail.configuration.beforeSqlScripts,
afterSqlScripts: detail.configuration.afterSqlScripts,
targetBeforeSqlScripts: detail.configuration.targetBeforeSqlScripts,
targetAfterSqlScripts: detail.configuration.targetAfterSqlScripts,
}
} else {
if (res.data.message) {

View File

@@ -10,6 +10,8 @@
package org.dromara.dbswitch.admin.controller.converter;
import cn.hutool.extra.spring.SpringUtil;
import java.util.ArrayList;
import java.util.Optional;
import org.dromara.dbswitch.admin.dao.AssignmentConfigDAO;
import org.dromara.dbswitch.admin.dao.DatabaseConnectionDAO;
import org.dromara.dbswitch.admin.entity.AssignmentConfigEntity;
@@ -43,6 +45,9 @@ public class AssignmentDetailConverter extends
? IncludeExcludeEnum.EXCLUDE
: IncludeExcludeEnum.INCLUDE);
config.setSourceTables(taskConfig.getSourceTables());
config.setIncrTableColumns(Optional.ofNullable(taskConfig.getIncrTableColumns()).orElseGet(ArrayList::new));
config.setSourceBeforeSqlScripts(taskConfig.getPreSqlScripts());
config.setSourceAfterSqlScripts(taskConfig.getPostSqlScripts());
config.setTargetConnectionId(dstConn.getId());
config.setTargetConnectionName(dstConn.getName());
config.setTargetTypeName(dstConn.getType().getName());
@@ -52,13 +57,13 @@ public class AssignmentDetailConverter extends
config.setTargetAutoIncrement(taskConfig.getTargetAutoIncrement());
config.setTableNameCase(taskConfig.getTableNameCase());
config.setColumnNameCase(taskConfig.getColumnNameCase());
config.setTableNameMapper(taskConfig.getTableNameMap());
config.setColumnNameMapper(taskConfig.getColumnNameMap());
config.setTableNameMapper(Optional.ofNullable(taskConfig.getTableNameMap()).orElseGet(ArrayList::new));
config.setColumnNameMapper(Optional.ofNullable(taskConfig.getColumnNameMap()).orElseGet(ArrayList::new));
config.setBatchSize(taskConfig.getBatchSize());
config.setChannelSize(taskConfig.getChannelSize());
config.setTargetSyncOption(taskConfig.getTargetSyncOption());
config.setBeforeSqlScripts(taskConfig.getBeforeSqlScripts());
config.setAfterSqlScripts(taskConfig.getAfterSqlScripts());
config.setTargetBeforeSqlScripts(taskConfig.getBeforeSqlScripts());
config.setTargetAfterSqlScripts(taskConfig.getAfterSqlScripts());
AssignmentDetailResponse detailResponse = new AssignmentDetailResponse();
detailResponse.setId(assignmentTaskEntity.getId());

View File

@@ -15,8 +15,10 @@ import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.dbswitch.admin.handler.ListPatternHandler;
import org.dromara.dbswitch.admin.handler.ListTabColHandler;
import org.dromara.dbswitch.admin.handler.ListTypeHandler;
import org.dromara.dbswitch.common.entity.PatternMapper;
import org.dromara.dbswitch.common.entity.TableColumnPair;
import org.dromara.dbswitch.common.type.CaseConvertEnum;
import org.dromara.dbswitch.common.type.ProductTableEnum;
import org.dromara.dbswitch.common.type.SyncOptionEnum;
@@ -53,9 +55,18 @@ public class AssignmentConfigEntity {
@TableField(value = "source_tables", typeHandler = ListTypeHandler.class)
private List<String> sourceTables;
@TableField(value = "table_incr_columns", typeHandler = ListTabColHandler.class)
private List<TableColumnPair> incrTableColumns;
@TableField("excluded_flag")
private Boolean excludedFlag;
@TableField("pre_sql_scripts")
private String preSqlScripts;
@TableField("post_sql_scripts")
private String postSqlScripts;
@TableField("target_connection_id")
private Long targetConnectionId;

View File

@@ -0,0 +1,67 @@
package org.dromara.dbswitch.admin.handler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.dromara.dbswitch.common.entity.TableColumnPair;
import org.dromara.dbswitch.data.util.JsonUtils;
public class ListTabColHandler extends BaseTypeHandler<List<TableColumnPair>> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, List<TableColumnPair> list,
JdbcType jdbcType) throws SQLException {
ps.setString(i, list2string(list));
}
@Override
public List<TableColumnPair> getNullableResult(ResultSet rs, String columnName)
throws SQLException {
String r = rs.getString(columnName);
if (rs.wasNull()) {
return null;
}
return string2list(r);
}
@Override
public List<TableColumnPair> getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
String r = rs.getString(columnIndex);
if (rs.wasNull()) {
return null;
}
return string2list(r);
}
@Override
public List<TableColumnPair> getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
String r = cs.getString(columnIndex);
if (cs.wasNull()) {
return null;
}
return string2list(r);
}
private String list2string(List<TableColumnPair> list) {
if (list == null || list.isEmpty()) {
return null;
}
return JsonUtils.toJsonString(list);
}
private List<TableColumnPair> string2list(String str) {
if (str == null || str.isEmpty()) {
return new ArrayList<>();
}
return JsonUtils.toBeanList(str, TableColumnPair.class);
}
}

View File

@@ -9,16 +9,17 @@
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.admin.model.request;
import org.dromara.dbswitch.admin.entity.AssignmentConfigEntity;
import org.dromara.dbswitch.admin.type.IncludeExcludeEnum;
import org.dromara.dbswitch.common.entity.PatternMapper;
import org.dromara.dbswitch.common.type.CaseConvertEnum;
import org.dromara.dbswitch.common.type.ProductTableEnum;
import org.dromara.dbswitch.common.type.SyncOptionEnum;
import java.util.List;
import java.util.Objects;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.dromara.dbswitch.admin.entity.AssignmentConfigEntity;
import org.dromara.dbswitch.admin.type.IncludeExcludeEnum;
import org.dromara.dbswitch.common.entity.PatternMapper;
import org.dromara.dbswitch.common.entity.TableColumnPair;
import org.dromara.dbswitch.common.type.CaseConvertEnum;
import org.dromara.dbswitch.common.type.ProductTableEnum;
import org.dromara.dbswitch.common.type.SyncOptionEnum;
public class AssigmentBaseRequest {
@@ -31,6 +32,10 @@ public class AssigmentBaseRequest {
private ProductTableEnum tableType;
private IncludeExcludeEnum includeOrExclude;
private List<String> sourceTables;
private List<TableColumnPair> incrTableColumns;
private String sourceBeforeSqlScripts;
private String sourceAfterSqlScripts;
private Long targetConnectionId;
private String targetSchema;
private CaseConvertEnum tableNameCase;
@@ -41,8 +46,8 @@ public class AssigmentBaseRequest {
private Boolean targetOnlyCreate;
private Boolean targetAutoIncrement;
private SyncOptionEnum targetSyncOption;
private String beforeSqlScripts;
private String afterSqlScripts;
private String targetBeforeSqlScripts;
private String targetAfterSqlScripts;
private Integer batchSize;
private Integer channelSize;
}
@@ -54,9 +59,10 @@ public class AssigmentBaseRequest {
assignmentConfigEntity.setSourceSchema(config.getSourceSchema());
assignmentConfigEntity.setTableType(config.getTableType());
assignmentConfigEntity.setSourceTables(config.getSourceTables());
assignmentConfigEntity.setExcludedFlag(
config.getIncludeOrExclude() == IncludeExcludeEnum.EXCLUDE
);
assignmentConfigEntity.setIncrTableColumns(config.getIncrTableColumns());
assignmentConfigEntity.setExcludedFlag(getExcludedFlag(config.getIncludeOrExclude()));
assignmentConfigEntity.setPreSqlScripts(getTrimValueOrNull(config.getSourceBeforeSqlScripts()));
assignmentConfigEntity.setPostSqlScripts(getTrimValueOrNull(config.getSourceAfterSqlScripts()));
assignmentConfigEntity.setTargetConnectionId(config.getTargetConnectionId());
assignmentConfigEntity.setTargetSchema(config.getTargetSchema());
assignmentConfigEntity.setTableNameCase(config.getTableNameCase());
@@ -66,8 +72,8 @@ public class AssigmentBaseRequest {
assignmentConfigEntity.setTargetDropTable(config.getTargetDropTable());
assignmentConfigEntity.setTargetOnlyCreate(config.getTargetOnlyCreate());
assignmentConfigEntity.setTargetAutoIncrement(config.getTargetAutoIncrement());
assignmentConfigEntity.setBeforeSqlScripts(getTrimValueOrNull(config.getBeforeSqlScripts()));
assignmentConfigEntity.setAfterSqlScripts(getTrimValueOrNull(config.getAfterSqlScripts()));
assignmentConfigEntity.setBeforeSqlScripts(getTrimValueOrNull(config.getTargetBeforeSqlScripts()));
assignmentConfigEntity.setAfterSqlScripts(getTrimValueOrNull(config.getTargetAfterSqlScripts()));
assignmentConfigEntity.setTargetSyncOption(config.getTargetSyncOption());
assignmentConfigEntity.setBatchSize(getValueOrDefault(config.getBatchSize(), 10000));
assignmentConfigEntity.setChannelSize(getValueOrDefault(config.getChannelSize(), 100));
@@ -76,6 +82,10 @@ public class AssigmentBaseRequest {
return assignmentConfigEntity;
}
protected boolean getExcludedFlag(IncludeExcludeEnum includeOrExclude) {
return includeOrExclude == IncludeExcludeEnum.EXCLUDE;
}
protected int getValueOrDefault(Integer value, int defaultValue) {
return Objects.nonNull(value) ? value : defaultValue;
}

View File

@@ -19,6 +19,7 @@ import lombok.NoArgsConstructor;
import org.dromara.dbswitch.admin.type.IncludeExcludeEnum;
import org.dromara.dbswitch.admin.type.ScheduleModeEnum;
import org.dromara.dbswitch.common.entity.PatternMapper;
import org.dromara.dbswitch.common.entity.TableColumnPair;
import org.dromara.dbswitch.common.type.CaseConvertEnum;
import org.dromara.dbswitch.common.type.ProductTableEnum;
import org.dromara.dbswitch.common.type.SyncOptionEnum;
@@ -83,6 +84,15 @@ public class AssignmentDetailResponse {
@ApiModelProperty("配置的表名列表")
private List<String> sourceTables;
@ApiModelProperty("增量同步表配置")
private List<TableColumnPair> incrTableColumns;
@ApiModelProperty("源端同步前置执行SQL脚本")
private String sourceBeforeSqlScripts;
@ApiModelProperty("源端同步后置执行SQL脚本")
private String sourceAfterSqlScripts;
@ApiModelProperty("目的端连接ID")
private Long targetConnectionId;
@@ -125,10 +135,10 @@ public class AssignmentDetailResponse {
@ApiModelProperty("同步操作方法")
private SyncOptionEnum targetSyncOption;
@ApiModelProperty("同步前置执行SQL脚本")
private String beforeSqlScripts;
@ApiModelProperty("目标端同步前置执行SQL脚本")
private String targetBeforeSqlScripts;
@ApiModelProperty("同步后置执行SQL脚本")
private String afterSqlScripts;
@ApiModelProperty("目标端同步后置执行SQL脚本")
private String targetAfterSqlScripts;
}
}

View File

@@ -9,4 +9,7 @@ public class PreviewNameMapperResponse {
private String originalName;
private String targetName;
private String typeName;
private int fieldType;
private boolean canIncrement;
}

View File

@@ -9,6 +9,18 @@
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.admin.service;
import com.google.common.collect.Lists;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.dromara.dbswitch.admin.common.exception.DbswitchException;
import org.dromara.dbswitch.admin.common.response.PageResult;
import org.dromara.dbswitch.admin.common.response.Result;
@@ -20,6 +32,10 @@ import org.dromara.dbswitch.admin.dao.AssignmentConfigDAO;
import org.dromara.dbswitch.admin.dao.AssignmentJobDAO;
import org.dromara.dbswitch.admin.dao.AssignmentTaskDAO;
import org.dromara.dbswitch.admin.dao.DatabaseConnectionDAO;
import org.dromara.dbswitch.admin.entity.AssignmentConfigEntity;
import org.dromara.dbswitch.admin.entity.AssignmentJobEntity;
import org.dromara.dbswitch.admin.entity.AssignmentTaskEntity;
import org.dromara.dbswitch.admin.entity.DatabaseConnectionEntity;
import org.dromara.dbswitch.admin.model.request.AssigmentCreateRequest;
import org.dromara.dbswitch.admin.model.request.AssigmentUpdateRequest;
import org.dromara.dbswitch.admin.model.request.AssignmentSearchRequest;
@@ -30,27 +46,14 @@ import org.dromara.dbswitch.admin.type.JobStatusEnum;
import org.dromara.dbswitch.admin.type.ScheduleModeEnum;
import org.dromara.dbswitch.admin.util.ExcelUtils;
import org.dromara.dbswitch.admin.util.PageUtils;
import org.dromara.dbswitch.admin.entity.AssignmentConfigEntity;
import org.dromara.dbswitch.admin.entity.AssignmentJobEntity;
import org.dromara.dbswitch.admin.entity.AssignmentTaskEntity;
import org.dromara.dbswitch.admin.entity.DatabaseConnectionEntity;
import org.dromara.dbswitch.common.converter.ConverterFactory;
import org.dromara.dbswitch.common.entity.TableColumnPair;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import org.dromara.dbswitch.data.config.DbswichPropertiesConfiguration;
import org.dromara.dbswitch.data.entity.GlobalParamConfigProperties;
import org.dromara.dbswitch.data.entity.SourceDataSourceProperties;
import org.dromara.dbswitch.data.entity.TargetDataSourceProperties;
import org.dromara.dbswitch.data.util.JsonUtils;
import com.google.common.collect.Lists;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -291,7 +294,16 @@ public class AssignmentService {
}
}
sourceDataSourceProperties.setSourceSchema(sourceSchema);
sourceDataSourceProperties.setRegexTableMapper(assignmentConfigEntity.getTableNameMap());
Map<String, String> incrTableColumns = Optional
.ofNullable(assignmentConfigEntity.getIncrTableColumns())
.orElseGet(ArrayList::new)
.stream().collect(Collectors.toMap(TableColumnPair::getTableName,
TableColumnPair::getColumnName, (a, b) -> b));
sourceDataSourceProperties.setIncrTableColumns(incrTableColumns);
sourceDataSourceProperties.setBeforeSqlScripts(assignmentConfigEntity.getPreSqlScripts());
sourceDataSourceProperties.setAfterSqlScripts(assignmentConfigEntity.getPostSqlScripts());
sourceDataSourceProperties.setRegexColumnMapper(assignmentConfigEntity.getColumnNameMap());
sourceDataSourceProperties.setFetchSize(assignmentConfigEntity.getBatchSize());
sourceDataSourceProperties.setTableType(assignmentConfigEntity.getTableType().name());

View File

@@ -6,6 +6,7 @@ import org.dromara.dbswitch.admin.common.response.ResultCode;
import org.dromara.dbswitch.admin.model.request.PreviewColumnNameMapperRequest;
import org.dromara.dbswitch.admin.model.request.PreviewTableNameMapperRequest;
import org.dromara.dbswitch.admin.model.response.PreviewNameMapperResponse;
import org.dromara.dbswitch.common.util.JdbcTypesUtils;
import org.dromara.dbswitch.common.util.PatterNameUtils;
import org.dromara.dbswitch.core.schema.ColumnDescription;
import org.dromara.dbswitch.core.schema.TableDescription;
@@ -92,11 +93,17 @@ public class PatternMapperService {
result.add(PreviewNameMapperResponse.builder()
.originalName(cd.getFieldName())
.targetName(targetName)
.typeName(cd.getFieldTypeName())
.fieldType(cd.getFieldType())
.canIncrement(JdbcTypesUtils.isIncrement(cd.getFieldType()))
.build());
} else {
result.add(PreviewNameMapperResponse.builder()
.originalName(cd.getFieldName())
.targetName(STRING_DELETE)
.typeName(cd.getFieldTypeName())
.fieldType(cd.getFieldType())
.canIncrement(false)
.build());
}
}

View File

@@ -0,0 +1,3 @@
ALTER TABLE `DBSWITCH_ASSIGNMENT_CONFIG` ADD COLUMN `table_incr_columns` longtext NULL COMMENT '增量同步表的增量字段配置' AFTER `source_tables`;
ALTER TABLE `DBSWITCH_ASSIGNMENT_CONFIG` ADD COLUMN `pre_sql_scripts` longtext NULL COMMENT '源端查询的前置执行sql脚本' AFTER `excluded_flag`;
ALTER TABLE `DBSWITCH_ASSIGNMENT_CONFIG` ADD COLUMN `post_sql_scripts` longtext NULL COMMENT '源端查询的后置执行sql脚本' AFTER `pre_sql_scripts`;

View File

@@ -0,0 +1,3 @@
ALTER TABLE `DBSWITCH_ASSIGNMENT_CONFIG` ADD COLUMN `table_incr_columns` longtext NULL COMMENT '增量同步表的增量字段配置' AFTER `source_tables`;
ALTER TABLE `DBSWITCH_ASSIGNMENT_CONFIG` ADD COLUMN `pre_sql_scripts` longtext NULL COMMENT '源端查询的前置执行sql脚本' AFTER `excluded_flag`;
ALTER TABLE `DBSWITCH_ASSIGNMENT_CONFIG` ADD COLUMN `post_sql_scripts` longtext NULL COMMENT '源端查询的后置执行sql脚本' AFTER `pre_sql_scripts`;

View File

@@ -0,0 +1,8 @@
ALTER TABLE DBSWITCH_ASSIGNMENT_CONFIG ADD COLUMN "table_incr_columns" text DEFAULT NULL;
COMMENT ON COLUMN DBSWITCH_ASSIGNMENT_CONFIG."table_incr_columns" IS '增量同步表的增量字段配置';
ALTER TABLE DBSWITCH_ASSIGNMENT_CONFIG ADD COLUMN "pre_sql_scripts" text DEFAULT NULL;
COMMENT ON COLUMN DBSWITCH_ASSIGNMENT_CONFIG."pre_sql_scripts" IS '源端查询的前置执行sql脚本';
ALTER TABLE DBSWITCH_ASSIGNMENT_CONFIG ADD COLUMN "post_sql_scripts" text DEFAULT NULL;
COMMENT ON COLUMN DBSWITCH_ASSIGNMENT_CONFIG."post_sql_scripts" IS '源端查询的后置执行sql脚本';

View File

@@ -1 +1 @@
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>异构数据迁移工具</title><link href=/static/css/app.d33ad4fb58aa3965be23b1406c5bbf2a.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.a9dbc79ba288440179ac.js></script><script type=text/javascript src=/static/js/vendor.8200341f98478c8f7552.js></script><script type=text/javascript src=/static/js/app.53b0f665e2622d19aebd.js></script></body></html>
<!DOCTYPE html><html><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>异构数据迁移工具</title><link href=/static/css/app.a3d7b4f2f93029443982a91a6313cac6.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=/static/js/manifest.a8a01998e2f76cc4452e.js></script><script type=text/javascript src=/static/js/vendor.8200341f98478c8f7552.js></script><script type=text/javascript src=/static/js/app.5d37304a4b37a60ed915.js></script></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
webpackJsonp([19],{"8UbZ":function(e,t){},nrt7:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a={components:{commonInfo:n("na+p").a},data:function(){return{infoform:{id:0,name:"",description:"",scheduleMode:"MANUAL",cronExpression:"",sourceConnectionId:0,sourceTypeName:"MySQL",sourceSchema:"",runStatus:"",tableType:"TABLE",includeOrExclude:"",sourceTables:[],tableNameMapper:[],columnNameMapper:[],tableNameCase:"NONE",columnNameCase:"NONE",targetConnectionId:0,targetTypeName:"MySQL",targetDropTable:!0,targetOnlyCreate:!1,autoSyncMode:2,targetSchema:"",batchSize:5e3,channelSize:100,targetSyncOption:"INSERT_UPDATE_DELETE",beforeSqlScripts:"",afterSqlScripts:""}}},methods:{loadAssignmentDetail:function(){var e=this;this.$http.get("/dbswitch/admin/api/v1/assignment/detail/id/"+this.$route.query.id).then(function(t){if(0===t.data.code){var n=t.data.data,a=2;a=n.configuration.targetDropTable&&n.configuration.targetOnlyCreate?1:n.configuration.targetDropTable||n.configuration.targetOnlyCreate?2:0,e.infoform={id:n.id,name:n.name,description:n.description,scheduleMode:n.scheduleMode,cronExpression:n.cronExpression,sourceConnectionId:n.configuration.sourceConnectionId,sourceConnectionName:n.configuration.sourceConnectionName,sourceTypeName:n.configuration.sourceTypeName,sourceSchema:n.configuration.sourceSchema,tableType:n.configuration.tableType,includeOrExclude:n.configuration.includeOrExclude,sourceTables:n.configuration.sourceTables,incrTableColumns:n.configuration.incrTableColumns,sourceBeforeSqlScripts:n.configuration.sourceBeforeSqlScripts,sourceAfterSqlScripts:n.configuration.sourceAfterSqlScripts,tableNameMapper:n.configuration.tableNameMapper,columnNameMapper:n.configuration.columnNameMapper,tableNameCase:n.configuration.tableNameCase,columnNameCase:n.configuration.columnNameCase,targetConnectionId:n.configuration.targetConnectionId,targetConnectionName:n.configuration.targetConnectionName,targetTypeName:n.configuration.targetTypeName,targetDropTable:n.configuration.targetDropTable,targetOnlyCreate:n.configuration.targetOnlyCreate,targetAutoIncrement:n.configuration.targetAutoIncrement,autoSyncMode:a,targetSchema:n.configuration.targetSchema,batchSize:n.configuration.batchSize,channelSize:n.configuration.channelSize,targetSyncOption:n.configuration.targetSyncOption,targetBeforeSqlScripts:n.configuration.targetBeforeSqlScripts,targetAfterSqlScripts:n.configuration.targetAfterSqlScripts}}else t.data.message&&alert("查询任务失败,"+t.data.message)})},handleGoBack:function(){this.$router.go(-1)}},created:function(){this.loadAssignmentDetail()}},o={render:function(){var e=this.$createElement,t=this._self._c||e;return t("el-card",[t("div",{staticStyle:{"margin-top":"15px"}},[t("commonInfo",{attrs:{infoform:this.infoform}}),this._v(" "),t("el-button",{staticStyle:{margin:"12px 0px 20px",float:"right"},attrs:{type:"primary",size:"mini",icon:"el-icon-arrow-left"},on:{click:this.handleGoBack}},[this._v("\n 返回\n ")])],1)])},staticRenderFns:[]};var r=n("VU/8")(a,o,!1,function(e){n("8UbZ")},"data-v-4fb0c2ed",null);t.default=r.exports}});
//# sourceMappingURL=19.4bf0b055df93039efaa4.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
webpackJsonp([19],{TT6I:function(t,e){},WfA7:function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a={data:function(){return{dialogVisible:!1,loading:!0,connectionTypes:[],versionDrivers:[],isActive:-1}},methods:{loadConnectionTypes:function(){var t=this;this.$http({method:"GET",url:"/dbswitch/admin/api/v1/connection/types"}).then(function(e){0===e.data.code?(t.connectionTypes=e.data.data,t.handleChooseClick("MYSQL",0)):e.data.message&&alert("初始化数据库类型信息失败:"+e.data.message)})},handleChooseClick:function(t,e){var i=this;this.isActive=e,this.$http.get("/dbswitch/admin/api/v1/connection/"+t+"/drivers").then(function(t){0===t.data.code?i.versionDrivers=t.data.data:t.data.message&&alert("查询驱动版本信息失败,"+t.data.message)})},handleClose:function(t){this.$confirm("确认关闭?").then(function(e){t()}).catch(function(t){})},formatJarFileList:function(t,e){return t[e.property].join(";\n")}},created:function(){this.loadConnectionTypes()},beforeDestroy:function(){}},n={render:function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",[i("el-card",[i("div",{staticClass:"container"},[i("el-card",{staticClass:"box-card"},[i("div",{staticClass:"clearfix",attrs:{slot:"header",align:"center"},slot:"header"},[i("span",[i("b",[t._v("数据库产品类型列表")])])]),t._v(" "),i("div",{staticClass:"navsBox"},[i("el-scrollbar",{staticStyle:{height:"600px"}},[i("ul",t._l(t.connectionTypes,function(e,a){return i("li",{key:a,class:{active:a==t.isActive},on:{click:function(i){return t.handleChooseClick(e.type,a)}}},[t._v("["+t._s(e.id)+"] "+t._s(e.name)+"\n ")])}),0)])],1)]),t._v(" "),i("div",{staticClass:"contentBox"},[i("div",{staticStyle:{margin:"10px 5px"},attrs:{align:"right",width:"95%"}},[i("el-button",{attrs:{type:"primary",size:"mini",icon:"el-icon-document-add"},on:{click:function(e){t.dialogVisible=!0}}},[t._v("添加\n ")])],1),t._v(" "),i("el-table",{attrs:{"header-cell-style":{background:"#eef1f6",color:"#606266"},data:t.versionDrivers,size:"small",stripe:"",border:""}},[i("template",{slot:"empty"},[i("span",[t._v("单击左侧数据库类型来查看对应的驱动版本信息")])]),t._v(" "),i("el-table-column",{attrs:{property:"driverVersion",label:"驱动版本号","min-width":"30%"}}),t._v(" "),i("el-table-column",{attrs:{property:"driverClass",label:"驱动类名","min-width":"30%"}}),t._v(" "),i("el-table-column",{attrs:{property:"jarFiles",formatter:t.formatJarFileList,label:"驱动JAR名称","min-width":"40%"}})],2)],1)],1)]),t._v(" "),i("el-dialog",{attrs:{title:"添加说明",visible:t.dialogVisible,width:"40%","before-close":t.handleClose},on:{"update:visible":function(e){t.dialogVisible=e}}},[i("span",[t._v("请按照驱动路径所在的目录${DBSWITCH_HOME}/drivers下在数据库类型为名称的目录下以驱动版本号为名称创建目录并放置对应的驱动jar文件然后重启即可生效。具体可参考https://gitee.com/inrgihc/dbswitch/tree/master/drivers下的目录结构。")]),t._v(" "),i("span"),t._v(" "),i("span",[t._v("特殊说明驱动版本目录下的所有JAR必须无任何外部依赖否则也需将其依赖JAR一起放置到对应的目录下。")]),t._v(" "),i("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[i("el-button",{on:{click:function(e){t.dialogVisible=!1}}},[t._v("取 消")]),t._v(" "),i("el-button",{attrs:{type:"primary"},on:{click:function(e){t.dialogVisible=!1}}},[t._v("确 定")])],1)])],1)},staticRenderFns:[]};var s=i("VU/8")(a,n,!1,function(t){i("TT6I")},"data-v-34e9157e",null);e.default=s.exports}});
//# sourceMappingURL=19.dd345b856062984bc737.js.map
webpackJsonp([20],{TT6I:function(t,e){},WfA7:function(t,e,i){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var a={data:function(){return{dialogVisible:!1,loading:!0,connectionTypes:[],versionDrivers:[],isActive:-1}},methods:{loadConnectionTypes:function(){var t=this;this.$http({method:"GET",url:"/dbswitch/admin/api/v1/connection/types"}).then(function(e){0===e.data.code?(t.connectionTypes=e.data.data,t.handleChooseClick("MYSQL",0)):e.data.message&&alert("初始化数据库类型信息失败:"+e.data.message)})},handleChooseClick:function(t,e){var i=this;this.isActive=e,this.$http.get("/dbswitch/admin/api/v1/connection/"+t+"/drivers").then(function(t){0===t.data.code?i.versionDrivers=t.data.data:t.data.message&&alert("查询驱动版本信息失败,"+t.data.message)})},handleClose:function(t){this.$confirm("确认关闭?").then(function(e){t()}).catch(function(t){})},formatJarFileList:function(t,e){return t[e.property].join(";\n")}},created:function(){this.loadConnectionTypes()},beforeDestroy:function(){}},n={render:function(){var t=this,e=t.$createElement,i=t._self._c||e;return i("div",[i("el-card",[i("div",{staticClass:"container"},[i("el-card",{staticClass:"box-card"},[i("div",{staticClass:"clearfix",attrs:{slot:"header",align:"center"},slot:"header"},[i("span",[i("b",[t._v("数据库产品类型列表")])])]),t._v(" "),i("div",{staticClass:"navsBox"},[i("el-scrollbar",{staticStyle:{height:"600px"}},[i("ul",t._l(t.connectionTypes,function(e,a){return i("li",{key:a,class:{active:a==t.isActive},on:{click:function(i){return t.handleChooseClick(e.type,a)}}},[t._v("["+t._s(e.id)+"] "+t._s(e.name)+"\n ")])}),0)])],1)]),t._v(" "),i("div",{staticClass:"contentBox"},[i("div",{staticStyle:{margin:"10px 5px"},attrs:{align:"right",width:"95%"}},[i("el-button",{attrs:{type:"primary",size:"mini",icon:"el-icon-document-add"},on:{click:function(e){t.dialogVisible=!0}}},[t._v("添加\n ")])],1),t._v(" "),i("el-table",{attrs:{"header-cell-style":{background:"#eef1f6",color:"#606266"},data:t.versionDrivers,size:"small",stripe:"",border:""}},[i("template",{slot:"empty"},[i("span",[t._v("单击左侧数据库类型来查看对应的驱动版本信息")])]),t._v(" "),i("el-table-column",{attrs:{property:"driverVersion",label:"驱动版本号","min-width":"30%"}}),t._v(" "),i("el-table-column",{attrs:{property:"driverClass",label:"驱动类名","min-width":"30%"}}),t._v(" "),i("el-table-column",{attrs:{property:"jarFiles",formatter:t.formatJarFileList,label:"驱动JAR名称","min-width":"40%"}})],2)],1)],1)]),t._v(" "),i("el-dialog",{attrs:{title:"添加说明",visible:t.dialogVisible,width:"40%","before-close":t.handleClose},on:{"update:visible":function(e){t.dialogVisible=e}}},[i("span",[t._v("请按照驱动路径所在的目录${DBSWITCH_HOME}/drivers下在数据库类型为名称的目录下以驱动版本号为名称创建目录并放置对应的驱动jar文件然后重启即可生效。具体可参考https://gitee.com/inrgihc/dbswitch/tree/master/drivers下的目录结构。")]),t._v(" "),i("span"),t._v(" "),i("span",[t._v("特殊说明驱动版本目录下的所有JAR必须无任何外部依赖否则也需将其依赖JAR一起放置到对应的目录下。")]),t._v(" "),i("span",{staticClass:"dialog-footer",attrs:{slot:"footer"},slot:"footer"},[i("el-button",{on:{click:function(e){t.dialogVisible=!1}}},[t._v("取 消")]),t._v(" "),i("el-button",{attrs:{type:"primary"},on:{click:function(e){t.dialogVisible=!1}}},[t._v("确 定")])],1)])],1)},staticRenderFns:[]};var s=i("VU/8")(a,n,!1,function(t){i("TT6I")},"data-v-34e9157e",null);e.default=s.exports}});
//# sourceMappingURL=20.0b81af042044c5cf2ee5.js.map

View File

@@ -1,2 +0,0 @@
webpackJsonp([20],{PQzB:function(e,t){},nrt7:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a={components:{commonInfo:n("na+p").a},data:function(){return{infoform:{id:0,name:"",description:"",scheduleMode:"MANUAL",cronExpression:"",sourceConnectionId:0,sourceTypeName:"MySQL",sourceSchema:"",runStatus:"",tableType:"TABLE",includeOrExclude:"",sourceTables:[],tableNameMapper:[],columnNameMapper:[],tableNameCase:"NONE",columnNameCase:"NONE",targetConnectionId:0,targetTypeName:"MySQL",targetDropTable:!0,targetOnlyCreate:!1,autoSyncMode:2,targetSchema:"",batchSize:5e3,channelSize:100,targetSyncOption:"INSERT_UPDATE_DELETE",beforeSqlScripts:"",afterSqlScripts:""}}},methods:{loadAssignmentDetail:function(){var e=this;this.$http.get("/dbswitch/admin/api/v1/assignment/detail/id/"+this.$route.query.id).then(function(t){if(0===t.data.code){var n=t.data.data,a=2;a=n.configuration.targetDropTable&&n.configuration.targetOnlyCreate?1:n.configuration.targetDropTable||n.configuration.targetOnlyCreate?2:0,e.infoform={id:n.id,name:n.name,description:n.description,scheduleMode:n.scheduleMode,cronExpression:n.cronExpression,sourceConnectionId:n.configuration.sourceConnectionId,sourceConnectionName:n.configuration.sourceConnectionName,sourceTypeName:n.configuration.sourceTypeName,sourceSchema:n.configuration.sourceSchema,tableType:n.configuration.tableType,includeOrExclude:n.configuration.includeOrExclude,sourceTables:n.configuration.sourceTables,tableNameMapper:n.configuration.tableNameMapper,columnNameMapper:n.configuration.columnNameMapper,tableNameCase:n.configuration.tableNameCase,columnNameCase:n.configuration.columnNameCase,targetConnectionId:n.configuration.targetConnectionId,targetConnectionName:n.configuration.targetConnectionName,targetTypeName:n.configuration.targetTypeName,targetDropTable:n.configuration.targetDropTable,targetOnlyCreate:n.configuration.targetOnlyCreate,targetAutoIncrement:n.configuration.targetAutoIncrement,autoSyncMode:a,targetSchema:n.configuration.targetSchema,batchSize:n.configuration.batchSize,channelSize:n.configuration.channelSize,targetSyncOption:n.configuration.targetSyncOption,beforeSqlScripts:n.configuration.beforeSqlScripts,afterSqlScripts:n.configuration.afterSqlScripts}}else t.data.message&&alert("查询任务失败,"+t.data.message)})},handleGoBack:function(){this.$router.go(-1)}},created:function(){this.loadAssignmentDetail()}},o={render:function(){var e=this.$createElement,t=this._self._c||e;return t("el-card",[t("div",{staticStyle:{"margin-top":"15px"}},[t("commonInfo",{attrs:{infoform:this.infoform}}),this._v(" "),t("el-button",{staticStyle:{margin:"12px 0px 20px",float:"right"},attrs:{type:"primary",size:"mini",icon:"el-icon-arrow-left"},on:{click:this.handleGoBack}},[this._v("\n 返回\n ")])],1)])},staticRenderFns:[]};var r=n("VU/8")(a,o,!1,function(e){n("PQzB")},"data-v-300f4796",null);t.default=r.exports}});
//# sourceMappingURL=20.7ca0c8dd914241082aaa.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,2 @@
webpackJsonp([24],{NHnr:function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=t("//Fk"),i=t.n(o),c=t("7+uW"),r={render:function(){var n=this.$createElement,e=this._self._c||n;return e("div",{staticClass:"body-wrapper"},[e("router-view")],1)},staticRenderFns:[]};var a=t("VU/8")({name:"App"},r,!1,function(n){t("O8ez")},"data-v-a97617c2",null).exports,l=t("/ocq");c.default.use(l.a);var u=new l.a({routes:[{path:"/",name:"首页",component:function(){return t.e(5).then(t.bind(null,"4er+"))},redirect:"/dashboard",children:[{path:"/dashboard",name:"系统概览",icon:"el-icon-menu",component:function(){return Promise.all([t.e(0),t.e(11)]).then(t.bind(null,"ARoL"))}},{path:"/connection",name:"连接管理",icon:"el-icon-s-order",component:function(){return t.e(10).then(t.bind(null,"Hoc+"))},children:[{path:"/connection/list",name:"数据源",icon:"el-icon-bank-card",component:function(){return Promise.all([t.e(0),t.e(22)]).then(t.bind(null,"qdtB"))}},{path:"/connection/driver",name:"驱动配置",icon:"el-icon-help",component:function(){return t.e(19).then(t.bind(null,"WfA7"))}}]},{path:"/metadata",name:"数据导航",icon:"el-icon-coin",component:function(){return Promise.all([t.e(0),t.e(1)]).then(t.bind(null,"PJ2q"))}},{path:"/task",name:"迁移任务",icon:"el-icon-s-tools",component:function(){return t.e(8).then(t.bind(null,"4KEO"))},children:[{path:"/task/list",name:"任务管理",icon:"el-icon-eleme",component:function(){return Promise.all([t.e(0),t.e(12)]).then(t.bind(null,"fOjX"))}},{path:"/task/schedule",name:"监控调度",icon:"el-icon-pie-chart",component:function(){return Promise.all([t.e(0),t.e(18)]).then(t.bind(null,"mKp/"))}}]},{path:"/log",name:"审计日志",icon:"el-icon-platform-eleme",component:function(){return t.e(9).then(t.bind(null,"QWih"))},children:[{path:"/log/access",name:"登录日志",icon:"el-icon-place",component:function(){return t.e(15).then(t.bind(null,"oQRv"))}},{path:"/log/action",name:"操作日志",icon:"el-icon-s-check",component:function(){return t.e(14).then(t.bind(null,"0eSS"))}}]},{path:"/about",name:"关于系统",icon:"el-icon-s-custom",component:function(){return t.e(4).then(t.bind(null,"m25N"))}},{path:"/user/personal",name:"个人中心",hidden:!0,component:function(){return t.e(2).then(t.bind(null,"uTKz"))}},{path:"/task/create",name:"创建任务",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(6)]).then(t.bind(null,"/rCC"))}},{path:"/task/update",name:"修改任务",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(7)]).then(t.bind(null,"txod"))}},{path:"/task/detail",name:"查看任务",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(20)]).then(t.bind(null,"nrt7"))}},{path:"/connection/list/select",name:"选择数据源类型",icon:"el-icon-menu",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(16)]).then(t.bind(null,"QftJ"))}},{path:"/connection/list/create",name:"创建数据源",icon:"el-icon-menu",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(21)]).then(t.bind(null,"2+MG"))}},{path:"/connection/list/detail",name:"数据源详情",icon:"el-icon-menu",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(13)]).then(t.bind(null,"b9UL"))}},{path:"/connection/list/update",name:"编辑数据源",icon:"el-icon-menu",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(17)]).then(t.bind(null,"LAon"))}}]},{path:"/login",name:"登录",component:function(){return t.e(3).then(t.bind(null,"T+/8"))}}]}),d=t("mtWM"),p=t.n(d).a.create();p.interceptors.request.use(function(n){return n.url=""+n.url,n});var m=p,s=t("zL8q"),h=t.n(s),f=(t("muQq"),t("tvR6"),t("7Vno")),b=t.n(f),v=t("XLwt"),P=t.n(v);c.default.use(m),c.default.use(h.a),c.default.use(b.a),c.default.prototype.$http=m,c.default.config.productionTip=!1,c.default.prototype.$echarts=P.a,m.interceptors.request.use(function(n){var e=sessionStorage.getItem("token");return e&&(n.headers.Authorization="Bearer "+e),n},function(n){return i.a.reject(n)}),m.interceptors.response.use(function(n){return!n.data||401!==n.data.code&&403!==n.data.code&&404!==n.data.code||u.push({path:"/login"}),n},function(n){return console.log(n),i.a.reject(n.response)}),new c.default({el:"#app",router:u,components:{App:a},template:"<App/>"})},O8ez:function(n,e){},muQq:function(n,e){},tvR6:function(n,e){}},["NHnr"]);
//# sourceMappingURL=app.53b0f665e2622d19aebd.js.map
webpackJsonp([24],{NHnr:function(n,e,t){"use strict";Object.defineProperty(e,"__esModule",{value:!0});var o=t("//Fk"),i=t.n(o),c=t("7+uW"),r={render:function(){var n=this.$createElement,e=this._self._c||n;return e("div",{staticClass:"body-wrapper"},[e("router-view")],1)},staticRenderFns:[]};var a=t("VU/8")({name:"App"},r,!1,function(n){t("O8ez")},"data-v-a97617c2",null).exports,l=t("/ocq");c.default.use(l.a);var u=new l.a({routes:[{path:"/",name:"首页",component:function(){return t.e(5).then(t.bind(null,"4er+"))},redirect:"/dashboard",children:[{path:"/dashboard",name:"系统概览",icon:"el-icon-menu",component:function(){return Promise.all([t.e(0),t.e(11)]).then(t.bind(null,"ARoL"))}},{path:"/connection",name:"连接管理",icon:"el-icon-s-order",component:function(){return t.e(10).then(t.bind(null,"Hoc+"))},children:[{path:"/connection/list",name:"数据源",icon:"el-icon-bank-card",component:function(){return Promise.all([t.e(0),t.e(22)]).then(t.bind(null,"qdtB"))}},{path:"/connection/driver",name:"驱动配置",icon:"el-icon-help",component:function(){return t.e(20).then(t.bind(null,"WfA7"))}}]},{path:"/metadata",name:"数据导航",icon:"el-icon-coin",component:function(){return Promise.all([t.e(0),t.e(1)]).then(t.bind(null,"PJ2q"))}},{path:"/task",name:"迁移任务",icon:"el-icon-s-tools",component:function(){return t.e(8).then(t.bind(null,"4KEO"))},children:[{path:"/task/list",name:"任务管理",icon:"el-icon-eleme",component:function(){return Promise.all([t.e(0),t.e(12)]).then(t.bind(null,"fOjX"))}},{path:"/task/schedule",name:"监控调度",icon:"el-icon-pie-chart",component:function(){return Promise.all([t.e(0),t.e(18)]).then(t.bind(null,"mKp/"))}}]},{path:"/log",name:"审计日志",icon:"el-icon-platform-eleme",component:function(){return t.e(9).then(t.bind(null,"QWih"))},children:[{path:"/log/access",name:"登录日志",icon:"el-icon-place",component:function(){return t.e(15).then(t.bind(null,"oQRv"))}},{path:"/log/action",name:"操作日志",icon:"el-icon-s-check",component:function(){return t.e(14).then(t.bind(null,"0eSS"))}}]},{path:"/about",name:"关于系统",icon:"el-icon-s-custom",component:function(){return t.e(4).then(t.bind(null,"m25N"))}},{path:"/user/personal",name:"个人中心",hidden:!0,component:function(){return t.e(2).then(t.bind(null,"uTKz"))}},{path:"/task/create",name:"创建任务",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(6)]).then(t.bind(null,"/rCC"))}},{path:"/task/update",name:"修改任务",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(7)]).then(t.bind(null,"txod"))}},{path:"/task/detail",name:"查看任务",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(19)]).then(t.bind(null,"nrt7"))}},{path:"/connection/list/select",name:"选择数据源类型",icon:"el-icon-menu",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(16)]).then(t.bind(null,"QftJ"))}},{path:"/connection/list/create",name:"创建数据源",icon:"el-icon-menu",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(21)]).then(t.bind(null,"2+MG"))}},{path:"/connection/list/detail",name:"数据源详情",icon:"el-icon-menu",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(13)]).then(t.bind(null,"b9UL"))}},{path:"/connection/list/update",name:"编辑数据源",icon:"el-icon-menu",hidden:!0,component:function(){return Promise.all([t.e(0),t.e(17)]).then(t.bind(null,"LAon"))}}]},{path:"/login",name:"登录",component:function(){return t.e(3).then(t.bind(null,"T+/8"))}}]}),d=t("mtWM"),p=t.n(d).a.create();p.interceptors.request.use(function(n){return n.url=""+n.url,n});var m=p,s=t("zL8q"),h=t.n(s),f=(t("muQq"),t("tvR6"),t("7Vno")),b=t.n(f),v=t("XLwt"),P=t.n(v);c.default.use(m),c.default.use(h.a),c.default.use(b.a),c.default.prototype.$http=m,c.default.config.productionTip=!1,c.default.prototype.$echarts=P.a,m.interceptors.request.use(function(n){var e=sessionStorage.getItem("token");return e&&(n.headers.Authorization="Bearer "+e),n},function(n){return i.a.reject(n)}),m.interceptors.response.use(function(n){return!n.data||401!==n.data.code&&403!==n.data.code&&404!==n.data.code||u.push({path:"/login"}),n},function(n){return console.log(n),i.a.reject(n.response)}),new c.default({el:"#app",router:u,components:{App:a},template:"<App/>"})},O8ez:function(n,e){},muQq:function(n,e){},tvR6:function(n,e){}},["NHnr"]);
//# sourceMappingURL=app.5d37304a4b37a60ed915.js.map

View File

@@ -0,0 +1,2 @@
!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,o){for(var a,d,i,u=0,b=[];u<r.length;u++)d=r[u],t[d]&&b.push(t[d][0]),t[d]=0;for(a in c)Object.prototype.hasOwnProperty.call(c,a)&&(e[a]=c[a]);for(n&&n(r,c,o);b.length;)b.shift()();if(o)for(u=0;u<o.length;u++)i=f(f.s=o[u]);return i};var r={},t={25:0};function f(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,f){n=t[e]=[r,f]});n[2]=r;var c=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.timeout=12e4,f.nc&&o.setAttribute("nonce",f.nc),o.src=f.p+"static/js/"+e+"."+{0:"28d822f5a0a5641b09f0",1:"fed755dff73c923d7f16",2:"22e821ec8c9909429f09",3:"ef5e983cf9ad9dc27899",4:"9489240be335f7dfd6fc",5:"de32c8bd9fcd7cba61b2",6:"dfd3b341f1e4a152e91d",7:"21157c8f69d215a18fbc",8:"fcf46f880cde60008573",9:"7482dae9c15b18627401",10:"80e7801109b57f1a3d72",11:"1b5e1420657152e07dc2",12:"22581e72d785a9570716",13:"8abd397b1c843e4a324b",14:"2ee83d5f12f903758c8e",15:"3273f138ad4a1f56a1a5",16:"66f213fc135ae6a5d988",17:"bfc722f22acf2bd97223",18:"5740050d081e1d16d980",19:"4bf0b055df93039efaa4",20:"0b81af042044c5cf2ee5",21:"72a8d707df810f529d2d",22:"a288f49b9b1648c38baf"}[e]+".js";var a=setTimeout(d,12e4);function d(){o.onerror=o.onload=null,clearTimeout(a);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return o.onerror=o.onload=d,c.appendChild(o),r},f.m=e,f.c=r,f.d=function(e,n,r){f.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},f.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(n,"a",n),n},f.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},f.p="/",f.oe=function(e){throw console.error(e),e}}([]);
//# sourceMappingURL=manifest.a8a01998e2f76cc4452e.js.map

View File

@@ -1,2 +0,0 @@
!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,a,o){for(var f,d,i,u=0,b=[];u<r.length;u++)d=r[u],t[d]&&b.push(t[d][0]),t[d]=0;for(f in a)Object.prototype.hasOwnProperty.call(a,f)&&(e[f]=a[f]);for(n&&n(r,a,o);b.length;)b.shift()();if(o)for(u=0;u<o.length;u++)i=c(c.s=o[u]);return i};var r={},t={25:0};function c(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,c),t.l=!0,t.exports}c.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,c){n=t[e]=[r,c]});n[2]=r;var a=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.timeout=12e4,c.nc&&o.setAttribute("nonce",c.nc),o.src=c.p+"static/js/"+e+"."+{0:"f6fb71bfc35a65544e6a",1:"fed755dff73c923d7f16",2:"22e821ec8c9909429f09",3:"ef5e983cf9ad9dc27899",4:"9489240be335f7dfd6fc",5:"de32c8bd9fcd7cba61b2",6:"b86dac5924401e544e1b",7:"5af9ccaa5430b74601db",8:"fcf46f880cde60008573",9:"7482dae9c15b18627401",10:"80e7801109b57f1a3d72",11:"1b5e1420657152e07dc2",12:"22581e72d785a9570716",13:"8abd397b1c843e4a324b",14:"2ee83d5f12f903758c8e",15:"3273f138ad4a1f56a1a5",16:"66f213fc135ae6a5d988",17:"bfc722f22acf2bd97223",18:"5740050d081e1d16d980",19:"dd345b856062984bc737",20:"7ca0c8dd914241082aaa",21:"72a8d707df810f529d2d",22:"a288f49b9b1648c38baf"}[e]+".js";var f=setTimeout(d,12e4);function d(){o.onerror=o.onload=null,clearTimeout(f);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return o.onerror=o.onload=d,a.appendChild(o),r},c.m=e,c.c=r,c.d=function(e,n,r){c.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},c.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return c.d(n,"a",n),n},c.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},c.p="/",c.oe=function(e){throw console.error(e),e}}([]);
//# sourceMappingURL=manifest.a9dbc79ba288440179ac.js.map

View File

@@ -0,0 +1,48 @@
// Copyright tang. All rights reserved.
// https://gitee.com/inrgihc/dbswitch
//
// Use of this source code is governed by a BSD-style license
//
// Author: tang (inrgihc@126.com)
// Date : 2020/1/2
// Location: beijing , china
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.common.entity;
import org.apache.commons.lang3.StringUtils;
public class IncrementPoint {
public static IncrementPoint EMPTY = new IncrementPoint();
private String columnName;
private Object maxValue;
private IncrementPoint() {
}
public IncrementPoint(String columnName, Object maxValue) {
this.columnName = columnName;
this.maxValue = maxValue;
}
public boolean isWorkable() {
return StringUtils.isNotBlank(columnName) && null != maxValue;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
public Object getMaxValue() {
return maxValue;
}
public void setMaxValue(Object maxValue) {
this.maxValue = maxValue;
}
}

View File

@@ -0,0 +1,23 @@
package org.dromara.dbswitch.common.entity;
public class TableColumnPair {
private String tableName;
private String columnName;
public String getTableName() {
return tableName;
}
public void setTableName(String tableName) {
this.tableName = tableName;
}
public String getColumnName() {
return columnName;
}
public void setColumnName(String columnName) {
this.columnName = columnName;
}
}

View File

@@ -121,10 +121,19 @@ public final class JdbcTypesUtils {
|| Types.LONGVARBINARY == sqlType);
}
public static boolean isTextable(int sqlType) {
public static boolean isTextile(int sqlType) {
return isNumeric(sqlType) || isString(sqlType) || isDateTime(sqlType) || isBoolean(sqlType);
}
public static boolean isIncrement(int sqlType) {
return (Types.BIT == sqlType
|| Types.BIGINT == sqlType
|| Types.INTEGER == sqlType
|| Types.TIMESTAMP == sqlType
|| Types.TIME_WITH_TIMEZONE == sqlType
|| Types.TIMESTAMP_WITH_TIMEZONE == sqlType);
}
// 其他类型如下9个
// JAVA_OBJECT
// OTHER

View File

@@ -10,13 +10,6 @@
package org.dromara.dbswitch.core.provider.query;
import cn.hutool.core.util.HexUtil;
import org.dromara.dbswitch.common.consts.Constants;
import org.dromara.dbswitch.common.entity.ResultSetWrapper;
import org.dromara.dbswitch.common.util.ObjectCastUtils;
import org.dromara.dbswitch.core.features.ProductFeatures;
import org.dromara.dbswitch.core.provider.AbstractCommonProvider;
import org.dromara.dbswitch.core.provider.ProductFactoryProvider;
import org.dromara.dbswitch.core.schema.SchemaTableData;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
@@ -27,6 +20,15 @@ import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.dromara.dbswitch.common.consts.Constants;
import org.dromara.dbswitch.common.entity.IncrementPoint;
import org.dromara.dbswitch.common.entity.ResultSetWrapper;
import org.dromara.dbswitch.common.util.ObjectCastUtils;
import org.dromara.dbswitch.core.features.ProductFeatures;
import org.dromara.dbswitch.core.provider.AbstractCommonProvider;
import org.dromara.dbswitch.core.provider.ProductFactoryProvider;
import org.dromara.dbswitch.core.schema.ColumnValue;
import org.dromara.dbswitch.core.schema.SchemaTableData;
@Slf4j
public class DefaultTableDataQueryProvider
@@ -58,11 +60,17 @@ public class DefaultTableDataQueryProvider
@Override
public ResultSetWrapper queryTableData(String schemaName, String tableName, List<String> fields,
List<String> orders) {
IncrementPoint point, List<String> orders) {
StringBuilder sb = new StringBuilder("SELECT ");
sb.append(quoteName(StringUtils.join(fields, quoteName(","))));
sb.append(" FROM ");
sb.append(quoteSchemaTableName(schemaName, tableName));
if (IncrementPoint.EMPTY != point && point.isWorkable()) {
sb.append(" WHERE ");
sb.append(quoteName(point.getColumnName()));
sb.append(" > ");
sb.append(point.getMaxValue());
}
if (CollectionUtils.isNotEmpty(orders)) {
sb.append(" ORDER BY ");
sb.append(quoteName(StringUtils.join(orders, quoteName(","))));
@@ -136,6 +144,24 @@ public class DefaultTableDataQueryProvider
}
}
@Override
public ColumnValue queryFieldMaxValue(Connection connection, String schemaName, String tableName, String filedName) {
String fullTableName = quoteSchemaTableName(schemaName, tableName);
String querySQL = String.format("SELECT MAX(%s) FROM %s ", quoteName(filedName), fullTableName);
try (Statement st = connection.createStatement()) {
try (ResultSet rs = st.executeQuery(querySQL)) {
ResultSetMetaData m = rs.getMetaData();
int dataType = m.getColumnType(1);
if (rs.next()) {
return new ColumnValue(dataType, rs.getObject(1));
}
return null;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
protected void beforeExecuteQuery(Connection connection, String schema, String table) {
// nothing except for hive
}

View File

@@ -9,12 +9,14 @@
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.core.provider.query;
import org.dromara.dbswitch.common.entity.ResultSetWrapper;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import org.dromara.dbswitch.core.schema.SchemaTableData;
import java.sql.Connection;
import java.util.Collections;
import java.util.List;
import org.dromara.dbswitch.common.entity.IncrementPoint;
import org.dromara.dbswitch.common.entity.ResultSetWrapper;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import org.dromara.dbswitch.core.schema.ColumnValue;
import org.dromara.dbswitch.core.schema.SchemaTableData;
/**
* 表数据查询
@@ -51,7 +53,21 @@ public interface TableDataQueryProvider {
* @return 结果集包装对象
*/
default ResultSetWrapper queryTableData(String schemaName, String tableName, List<String> fields) {
return queryTableData(schemaName, tableName, fields, Collections.emptyList());
return queryTableData(schemaName, tableName, fields, IncrementPoint.EMPTY, Collections.emptyList());
}
/**
* 获取指定schema下表的结果集
*
* @param schemaName 模式名称
* @param tableName 表名称
* @param fields 字段列表
* @param point 增量点
* @return 结果集包装对象
*/
default ResultSetWrapper queryTableData(String schemaName, String tableName, List<String> fields,
IncrementPoint point) {
return queryTableData(schemaName, tableName, fields, point, Collections.emptyList());
}
/**
@@ -63,8 +79,23 @@ public interface TableDataQueryProvider {
* @param orders 排序字段列表
* @return 结果集包装对象
*/
default ResultSetWrapper queryTableData(String schemaName, String tableName, List<String> fields,
List<String> orders) {
return queryTableData(schemaName, tableName, fields, IncrementPoint.EMPTY, orders);
}
/**
* 获取指定schema下表的按主键有序的结果集
*
* @param schemaName 模式名称
* @param tableName 表名称
* @param fields 字段列表
* @param point 增量点
* @param orders 排序字段列表
* @return 结果集包装对象
*/
ResultSetWrapper queryTableData(String schemaName, String tableName, List<String> fields,
List<String> orders);
IncrementPoint point, List<String> orders);
/**
* 获取指定模式表内的数据
@@ -76,4 +107,17 @@ public interface TableDataQueryProvider {
* @return 数据内容
*/
SchemaTableData queryTableData(Connection connection, String schemaName, String tableName, int rowCount);
/**
* 查询增量字段数据的最大值
*
* @param connection JDBC连接
* @param schemaName 模式名称
* @param tableName 表名称
* @param filedName 字段名称
* @return 最大值(可能为null)
*/
default ColumnValue queryFieldMaxValue(Connection connection, String schemaName, String tableName, String filedName) {
return null;
}
}

View File

@@ -0,0 +1,35 @@
// Copyright tang. All rights reserved.
// https://gitee.com/inrgihc/dbswitch
//
// Use of this source code is governed by a BSD-style license
//
// Author: tang (inrgihc@126.com)
// Date : 2020/1/2
// Location: beijing , china
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.core.schema;
/**
* 数据库表列的值
*
* @author tang
*/
public class ColumnValue {
private int jdbcType;
private Object value;
public ColumnValue(int jdbcType, Object value) {
this.jdbcType = jdbcType;
this.value = value;
}
public int getJdbcType() {
return jdbcType;
}
public Object getValue() {
return value;
}
}

View File

@@ -9,23 +9,24 @@
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.core.service;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import javax.sql.DataSource;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import org.dromara.dbswitch.core.provider.ProductFactoryProvider;
import org.dromara.dbswitch.core.provider.ProductProviderFactory;
import org.dromara.dbswitch.core.provider.meta.MetadataProvider;
import org.dromara.dbswitch.core.provider.query.TableDataQueryProvider;
import org.dromara.dbswitch.core.schema.ColumnDescription;
import org.dromara.dbswitch.core.schema.ColumnValue;
import org.dromara.dbswitch.core.schema.IndexDescription;
import org.dromara.dbswitch.core.schema.SchemaTableData;
import org.dromara.dbswitch.core.schema.SchemaTableMeta;
import org.dromara.dbswitch.core.schema.TableDescription;
import org.dromara.dbswitch.core.schema.SourceProperties;
import org.dromara.dbswitch.core.schema.TableDescription;
import org.dromara.dbswitch.core.util.GenerateSqlUtils;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import javax.sql.DataSource;
/**
* 用DataSource对象的元数据获取服务
@@ -204,6 +205,15 @@ public class DefaultMetadataService implements MetadataService {
}
}
@Override
public ColumnValue queryIncrementPoint(String schemaName, String tableName, String filedName) {
try (Connection connection = dataSource.getConnection()) {
return dataQueryProvider.queryFieldMaxValue(connection, schemaName, tableName, filedName);
} catch (SQLException se) {
throw new RuntimeException(se);
}
}
@Override
public void testQuerySQL(String sql) {
try (Connection connection = dataSource.getConnection()) {

View File

@@ -9,15 +9,16 @@
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.core.service;
import java.util.List;
import javax.sql.DataSource;
import org.dromara.dbswitch.core.provider.meta.MetadataProvider;
import org.dromara.dbswitch.core.schema.ColumnDescription;
import org.dromara.dbswitch.core.schema.SourceProperties;
import org.dromara.dbswitch.core.schema.ColumnValue;
import org.dromara.dbswitch.core.schema.IndexDescription;
import org.dromara.dbswitch.core.schema.SchemaTableData;
import org.dromara.dbswitch.core.schema.SchemaTableMeta;
import org.dromara.dbswitch.core.schema.SourceProperties;
import org.dromara.dbswitch.core.schema.TableDescription;
import java.util.List;
import javax.sql.DataSource;
public interface MetadataService {
@@ -145,6 +146,17 @@ public interface MetadataService {
*/
SchemaTableData queryTableData(String schemaName, String tableName, int rowCount);
/**
* 查询增量字段数据的最大值
*
* @param schemaName 模式名称
* @param tableName 表名称
* @param filedName 字段名称
* @return 最大值(可能为null)
*/
ColumnValue queryIncrementPoint(String schemaName, String tableName, String filedName);
/**
* 根据字段结构信息组装对应数据库的建表DDL语句
*

View File

@@ -9,11 +9,13 @@
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.data.entity;
import org.dromara.dbswitch.common.entity.PatternMapper;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import lombok.Data;
import org.dromara.dbswitch.common.entity.PatternMapper;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
/**
* 源端参数配置
@@ -32,11 +34,15 @@ public class SourceDataSourceProperties {
private Long connectionTimeout = TimeUnit.SECONDS.toMillis(60);
private Long maxLifeTime = TimeUnit.MINUTES.toMillis(60);
private String beforeSqlScripts;
private String afterSqlScripts;
private Integer fetchSize = 5000;
private String sourceSchema = "";
private String tableType = "TABLE";
private String sourceIncludes = "";
private String sourceExcludes = "";
private List<PatternMapper> regexTableMapper;
private List<PatternMapper> regexColumnMapper;
private Map<String, String> incrTableColumns = Collections.emptyMap();
private List<PatternMapper> regexTableMapper = Collections.emptyList();
private List<PatternMapper> regexColumnMapper = Collections.emptyList();
}

View File

@@ -10,40 +10,6 @@
package org.dromara.dbswitch.data.handler;
import cn.hutool.core.io.unit.DataSizeUtil;
import org.dromara.dbswitch.core.calculate.DefaultChangeCalculatorService;
import org.dromara.dbswitch.core.calculate.RecordRowChangeCalculator;
import org.dromara.dbswitch.core.calculate.RecordRowHandler;
import org.dromara.dbswitch.core.calculate.RowChangeTypeEnum;
import org.dromara.dbswitch.core.calculate.TaskParamEntity;
import org.dromara.dbswitch.common.consts.Constants;
import org.dromara.dbswitch.common.entity.CloseableDataSource;
import org.dromara.dbswitch.common.entity.ResultSetWrapper;
import org.dromara.dbswitch.common.type.CaseConvertEnum;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import org.dromara.dbswitch.common.util.DatabaseAwareUtils;
import org.dromara.dbswitch.common.util.JdbcTypesUtils;
import org.dromara.dbswitch.common.util.PatterNameUtils;
import org.dromara.dbswitch.core.basic.exchange.BatchElement;
import org.dromara.dbswitch.core.basic.exchange.MemChannel;
import org.dromara.dbswitch.core.basic.task.TaskProcessor;
import org.dromara.dbswitch.data.config.DbswichPropertiesConfiguration;
import org.dromara.dbswitch.data.domain.ReaderTaskParam;
import org.dromara.dbswitch.data.domain.ReaderTaskResult;
import org.dromara.dbswitch.data.entity.SourceDataSourceProperties;
import org.dromara.dbswitch.data.entity.TargetDataSourceProperties;
import org.dromara.dbswitch.core.provider.ProductFactoryProvider;
import org.dromara.dbswitch.core.provider.ProductProviderFactory;
import org.dromara.dbswitch.core.provider.manage.TableManageProvider;
import org.dromara.dbswitch.core.provider.meta.MetadataProvider;
import org.dromara.dbswitch.core.provider.query.TableDataQueryProvider;
import org.dromara.dbswitch.core.provider.sync.TableDataSynchronizeProvider;
import org.dromara.dbswitch.core.provider.transform.RecordTransformProvider;
import org.dromara.dbswitch.core.provider.write.TableDataWriteProvider;
import org.dromara.dbswitch.core.schema.ColumnDescription;
import org.dromara.dbswitch.core.schema.TableDescription;
import org.dromara.dbswitch.core.schema.SourceProperties;
import org.dromara.dbswitch.core.service.DefaultMetadataService;
import org.dromara.dbswitch.core.service.MetadataService;
import com.google.common.collect.Lists;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
@@ -58,8 +24,44 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.dbswitch.common.consts.Constants;
import org.dromara.dbswitch.common.entity.CloseableDataSource;
import org.dromara.dbswitch.common.entity.IncrementPoint;
import org.dromara.dbswitch.common.entity.ResultSetWrapper;
import org.dromara.dbswitch.common.type.CaseConvertEnum;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import org.dromara.dbswitch.common.util.DatabaseAwareUtils;
import org.dromara.dbswitch.common.util.JdbcTypesUtils;
import org.dromara.dbswitch.common.util.PatterNameUtils;
import org.dromara.dbswitch.core.basic.exchange.BatchElement;
import org.dromara.dbswitch.core.basic.exchange.MemChannel;
import org.dromara.dbswitch.core.basic.task.TaskProcessor;
import org.dromara.dbswitch.core.calculate.DefaultChangeCalculatorService;
import org.dromara.dbswitch.core.calculate.RecordRowChangeCalculator;
import org.dromara.dbswitch.core.calculate.RecordRowHandler;
import org.dromara.dbswitch.core.calculate.RowChangeTypeEnum;
import org.dromara.dbswitch.core.calculate.TaskParamEntity;
import org.dromara.dbswitch.core.provider.ProductFactoryProvider;
import org.dromara.dbswitch.core.provider.ProductProviderFactory;
import org.dromara.dbswitch.core.provider.manage.TableManageProvider;
import org.dromara.dbswitch.core.provider.meta.MetadataProvider;
import org.dromara.dbswitch.core.provider.query.TableDataQueryProvider;
import org.dromara.dbswitch.core.provider.sync.TableDataSynchronizeProvider;
import org.dromara.dbswitch.core.provider.transform.RecordTransformProvider;
import org.dromara.dbswitch.core.provider.write.TableDataWriteProvider;
import org.dromara.dbswitch.core.schema.ColumnDescription;
import org.dromara.dbswitch.core.schema.ColumnValue;
import org.dromara.dbswitch.core.schema.SourceProperties;
import org.dromara.dbswitch.core.schema.TableDescription;
import org.dromara.dbswitch.core.service.DefaultMetadataService;
import org.dromara.dbswitch.core.service.MetadataService;
import org.dromara.dbswitch.data.config.DbswichPropertiesConfiguration;
import org.dromara.dbswitch.data.domain.ReaderTaskParam;
import org.dromara.dbswitch.data.domain.ReaderTaskResult;
import org.dromara.dbswitch.data.entity.SourceDataSourceProperties;
import org.dromara.dbswitch.data.entity.TargetDataSourceProperties;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.StringUtils;
/**
* 数据读取线程体(一个表的读)
@@ -87,6 +89,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
private String sourceTableRemarks;
private List<ColumnDescription> sourceColumnDescriptions;
private List<String> sourcePrimaryKeys;
private Map<String, String> incrTableColumns;
// 目的端
private final CloseableDataSource targetDataSource;
@@ -101,8 +104,8 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
private String tableNameMapString;
// 统计信息
AtomicLong totalBytes = new AtomicLong(0);
AtomicLong totalCount = new AtomicLong(0);
private AtomicLong totalBytes = new AtomicLong(0);
private AtomicLong totalCount = new AtomicLong(0);
private CountDownLatch robotCountDownLatch;
@@ -118,6 +121,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
this.targetProductType = this.targetProperties.getType();
this.sourceSchemaName = this.sourceProperties.getSourceSchema();
this.sourceTableName = this.tableDescription.getTableName();
this.incrTableColumns = this.sourceProperties.getIncrTableColumns();
this.targetExistTables = taskParam.getTargetExistTables();
this.robotCountDownLatch = taskParam.getCountDownLatch();
}
@@ -128,7 +132,6 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
fetchSize = sourceProperties.getFetchSize();
}
if (this.targetProductType.isLikeHive()) {
// !! hive does not support upper table name and upper column name
properties.getTarget().setTableNameCase(CaseConvertEnum.LOWER);
@@ -202,7 +205,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
for (int i = 0; i < sourceColumnDescriptions.size(); ++i) {
String sourceColumnName = sourceColumnDescriptions.get(i).getFieldName();
String targetColumnName = targetColumnDescriptions.get(i).getFieldName();
if (StringUtils.hasLength(targetColumnName)) {
if (StringUtils.isNotBlank(targetColumnName)) {
columnMapperPairs.add(String.format("%s --> %s", sourceColumnName, targetColumnName));
mapChecker.put(sourceColumnName, targetColumnName);
} else {
@@ -272,7 +275,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
List<String> sqlCreateTable = sourceMetaDataService.getDDLCreateTableSQL(
targetMetaProvider,
targetColumnDescriptions.stream()
.filter(column -> StringUtils.hasLength(column.getFieldName()))
.filter(column -> StringUtils.isNotBlank(column.getFieldName()))
.collect(Collectors.toList()),
targetPrimaryKeys,
targetSchemaName,
@@ -321,7 +324,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
List<String> sqlCreateTable = sourceMetaDataService.getDDLCreateTableSQL(
targetMetaProvider,
targetColumnDescriptions.stream()
.filter(column -> StringUtils.hasLength(column.getFieldName()))
.filter(column -> StringUtils.isNotBlank(column.getFieldName()))
.collect(Collectors.toList()),
targetPrimaryKeys,
targetSchemaName,
@@ -341,9 +344,29 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
return doFullCoverSynchronize(targetWriter, targetTableManager, sourceQuerier, transformProvider);
}
// 判断是否具备变化量同步的条件1两端表结构一致且都有一样的主键字段(2)MySQL使用Innodb引擎
if (properties.getTarget().getChangeDataSync()) {
// 根据主键情况判断同步的方式:增量同步或覆盖同步
if (incrTableColumns.containsKey(sourceTableName)) {
// 处理指定增量字段的增量同步
String incrSourceColumnName = this.incrTableColumns.get(sourceTableName);
String incrTargetColumnName = mapChecker.get(incrSourceColumnName);
if (org.apache.commons.lang3.StringUtils.isBlank(incrTargetColumnName)) {
throw new RuntimeException("增量字段在目标端表中不存在");
}
MetadataService service = new DefaultMetadataService(targetDataSource, targetProductType);
ColumnValue columnValue = service.queryIncrementPoint(targetSchemaName, targetTableName, incrTargetColumnName);
IncrementPoint incrPoint = IncrementPoint.EMPTY;
if (null != columnValue) {
if (!JdbcTypesUtils.isInteger(columnValue.getJdbcType())
&& !JdbcTypesUtils.isDateTime(columnValue.getJdbcType())) {
throw new RuntimeException("增量字段必须为整型或时间类型");
}
incrPoint = new IncrementPoint(incrSourceColumnName, columnValue.getValue());
}
log.info("Table: {}.{} has increment column: {}", sourceSchemaName, sourceTableName, incrSourceColumnName);
return doFullCoverSynchronize(targetWriter, targetTableManager, sourceQuerier, transformProvider, incrPoint);
} else if (properties.getTarget().getChangeDataSync()) {
log.info("Check table: {}.{} can whether use change data sync", sourceSchemaName, sourceTableName);
// 判断是否具备变化量同步的条件1两端表结构一致且都有一样的主键字段(2)MySQL使用Innodb引擎
// 根据主键情况判断同步的方式:变化量同步或覆盖同步
MetadataService metaDataByDatasourceService =
new DefaultMetadataService(targetDataSource, targetProductType);
List<String> dbTargetPks = metaDataByDatasourceService.queryTablePrimaryKeys(
@@ -357,12 +380,13 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
targetSchemaName, targetTableName, targetDataSource)) {
return doFullCoverSynchronize(targetWriter, targetTableManager, sourceQuerier, transformProvider);
} else {
return doIncreaseSynchronize(targetSynchronizer, transformProvider);
return doChangeSynchronize(targetSynchronizer, transformProvider);
}
} else {
return doFullCoverSynchronize(targetWriter, targetTableManager, sourceQuerier, transformProvider);
}
} else {
log.info("Table: {}.{} with target value changeDataSync=false", sourceSchemaName, sourceTableName);
return doFullCoverSynchronize(targetWriter, targetTableManager, sourceQuerier, transformProvider);
}
}
@@ -379,6 +403,21 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
*/
private ReaderTaskResult doFullCoverSynchronize(TableDataWriteProvider tableWriter, TableManageProvider tableManager,
TableDataQueryProvider sourceQuerier, RecordTransformProvider transformer) {
return doFullCoverSynchronize(tableWriter, tableManager, sourceQuerier, transformer, IncrementPoint.EMPTY);
}
/**
* 执行覆盖同步
*
* @param tableWriter
* @param tableManager
* @param sourceQuerier
* @param transformer
* @param incrementPoint
* @return ReaderTaskResult
*/
private ReaderTaskResult doFullCoverSynchronize(TableDataWriteProvider tableWriter, TableManageProvider tableManager,
TableDataQueryProvider sourceQuerier, RecordTransformProvider transformer, IncrementPoint incrementPoint) {
final int BATCH_SIZE = fetchSize;
List<String> sourceFields = new ArrayList<>();
@@ -394,16 +433,22 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
// 准备目的端的数据写入操作
tableWriter.prepareWrite(targetSchemaName, targetTableName, targetFields);
// 清空目的端表的数据
tableManager.truncateTableData(targetSchemaName, targetTableName);
// 增量模式不能清空目表的数据
if (IncrementPoint.EMPTY == incrementPoint) {
// 清空目的端表的数据
tableManager.truncateTableData(targetSchemaName, targetTableName);
}
// 查询源端数据并写入目的端
sourceQuerier.setQueryFetchSize(BATCH_SIZE);
ResultSetWrapper srs = sourceQuerier.queryTableData(
sourceSchemaName, sourceTableName, sourceFields
sourceSchemaName, sourceTableName, sourceFields, incrementPoint
);
String syncMethod = (IncrementPoint.EMPTY == incrementPoint)
? "FullCoverSync" : "IncrementSync";
List<Object[]> cache = new LinkedList<>();
long cacheBytes = 0;
try (ResultSet rs = srs.getResultSet()) {
@@ -435,8 +480,8 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
.tableNameMapString(tableNameMapString)
.handler((arg1, arg2, logger) -> {
long ret = tableWriter.write(arg1, arg2);
logger.info("[FullCoverSync] handle write table [{}] batch record count: {}, the bytes size: {}",
tableNameMapString, ret, DataSizeUtil.format(finalCacheBytes));
logger.info("[{}] handle write table [{}] batch record count: {}, the bytes size: {}",
syncMethod, tableNameMapString, ret, DataSizeUtil.format(finalCacheBytes));
return ret;
})
.arg1(Lists.newArrayList(targetFields))
@@ -456,8 +501,8 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
.tableNameMapString(tableNameMapString)
.handler((arg1, arg2, logger) -> {
long ret = tableWriter.write(arg1, arg2);
logger.info("[FullCoverSync] handle write table [{}] batch record count: {}, the bytes size: {}",
tableNameMapString, ret, DataSizeUtil.format(finalCacheBytes));
logger.info("[{}] handle write table [{}] batch record count: {}, the bytes size: {}",
syncMethod, tableNameMapString, ret, DataSizeUtil.format(finalCacheBytes));
return ret;
})
.arg1(Lists.newArrayList(targetFields))
@@ -468,10 +513,10 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
totalBytes.addAndGet(cacheBytes);
}
log.info("[FullCoverSync] handle read table [{}] total record count: {}, total bytes = {}",
tableNameMapString, totalCount.get(), DataSizeUtil.format(totalBytes.get()));
log.info("[{}] handle read table [{}] total record count: {}, total bytes = {}",
syncMethod, tableNameMapString, totalCount.get(), DataSizeUtil.format(totalBytes.get()));
} catch (Throwable e) {
log.warn("[FullCoverSync] handle read table [{}] error: {}", e.getMessage());
log.warn("[{}] handle read table [{}] error: {}", syncMethod, e.getMessage());
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
@@ -498,7 +543,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
* @param transformer
* @return ReaderTaskResult
*/
private ReaderTaskResult doIncreaseSynchronize(TableDataSynchronizeProvider synchronizer,
private ReaderTaskResult doChangeSynchronize(TableDataSynchronizeProvider synchronizer,
RecordTransformProvider transformer) {
final int BATCH_SIZE = fetchSize;
@@ -539,7 +584,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
calculator.setCheckJdbcType(false);
// 执行实际的变化同步过程
log.info("[IncreaseSync] Handle table by compare [{}] data now ... ", tableNameMapString);
log.info("[ChangeSync] Handle table by compare [{}] data now ... ", tableNameMapString);
calculator.executeCalculate(param, new RecordRowHandler() {
private long countInsert = 0;
@@ -627,7 +672,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
doUpdate(fields);
}
log.info("[IncreaseSync] Handle table by compare [{}] total count: {}, Insert:{},Update:{},Delete:{} ",
log.info("[ChangeSync] Handle table by compare [{}] total count: {}, Insert:{},Update:{},Delete:{} ",
tableNameMapString, countTotal, countInsert, countUpdate, countDelete);
}
@@ -637,7 +682,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
.tableNameMapString(tableNameMapString)
.handler((arg1, arg2, logger) -> {
long ret = synchronizer.executeInsert(arg2);
logger.info("[IncreaseSync] Handle write table [{}] record Insert count: {}",
logger.info("[ChangeSync] Handle write table [{}] record Insert count: {}",
tableNameMapString, ret);
return ret;
})
@@ -654,7 +699,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
.tableNameMapString(tableNameMapString)
.handler((arg1, arg2, logger) -> {
long ret = synchronizer.executeUpdate(arg2);
logger.info("[IncreaseSync] Handle write table [{}] record Update count: {}",
logger.info("[ChangeSync] Handle write table [{}] record Update count: {}",
tableNameMapString, ret);
return ret;
})
@@ -671,7 +716,7 @@ public class ReaderTaskThread extends TaskProcessor<ReaderTaskResult> {
.tableNameMapString(tableNameMapString)
.handler((arg1, arg2, logger) -> {
long ret = synchronizer.executeDelete(arg2);
logger.info("[IncreaseSync] Handle write table [{}] record Delete count: {}",
logger.info("[ChangeSync] Handle write table [{}] record Delete count: {}",
tableNameMapString, ret);
return ret;
})

View File

@@ -9,6 +9,13 @@
/////////////////////////////////////////////////////////////
package org.dromara.dbswitch.data.service;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.dromara.dbswitch.common.entity.CloseableDataSource;
import org.dromara.dbswitch.common.entity.LoggingRunnable;
import org.dromara.dbswitch.common.entity.MdcKeyValue;
@@ -20,13 +27,6 @@ import org.dromara.dbswitch.data.config.DbswichPropertiesConfiguration;
import org.dromara.dbswitch.data.entity.GlobalParamConfigProperties;
import org.dromara.dbswitch.data.util.DataSourceUtils;
import org.dromara.dbswitch.data.util.MachineUtils;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.stereotype.Service;
@@ -124,12 +124,19 @@ public class MigrationService implements Runnable {
try (CloseableDataSource sourceDataSource = DataSourceUtils.createSourceDataSource(configuration.getSource())) {
robotReader = new DefaultReaderRobot(mdcKeyValue, configuration, sourceDataSource, targetDataSource);
robotWriter = new DefaultWriterRobot(mdcKeyValue, robotReader, writeThreadNum, concurrentWrite);
boolean success = executeSqlScripts(targetDataSource, configuration.getTarget().getBeforeSqlScripts());
boolean sourceSuccess = executeSqlScripts(sourceDataSource, configuration.getSource().getBeforeSqlScripts());
try {
exchanger.exchange(robotReader, robotWriter);
boolean targetSuccess = executeSqlScripts(targetDataSource, configuration.getTarget().getBeforeSqlScripts());
try {
exchanger.exchange(robotReader, robotWriter);
} finally {
if (targetSuccess) {
executeSqlScripts(targetDataSource, configuration.getTarget().getAfterSqlScripts());
}
}
} finally {
if (success) {
executeSqlScripts(targetDataSource, configuration.getTarget().getAfterSqlScripts());
if (sourceSuccess) {
executeSqlScripts(sourceDataSource, configuration.getSource().getAfterSqlScripts());
}
}
}
@@ -160,7 +167,7 @@ public class MigrationService implements Runnable {
}
}
private boolean executeSqlScripts(CloseableDataSource targetDataSource, String sqlScripts) {
private boolean executeSqlScripts(CloseableDataSource closeableDataSource, String sqlScripts) {
if (StringUtils.isBlank(sqlScripts) || StringUtils.isBlank(sqlScripts.trim())) {
return true;
}
@@ -171,7 +178,7 @@ public class MigrationService implements Runnable {
sqlList);
if (!sqlList.isEmpty()) {
try {
try (Connection connection = targetDataSource.getConnection();
try (Connection connection = closeableDataSource.getConnection();
Statement statement = connection.createStatement()) {
for (String sql : sqlList) {
log.info("Execute sql : {}", sql);

View File

@@ -20,6 +20,9 @@ dbswitch:
source-includes: ''
## table name exclude from table lists, separate by ','
source-excludes: ''
## table incr column name for table data increment synchronization
incr-table-columns:
t_test_table: id
## table name convert mapper by regular expression
regex-table-mapper:
- from-pattern: '^'

View File

@@ -10,6 +10,7 @@
package org.dromara.dbswitch.product.elasticsearch;
import org.dromara.dbswitch.common.consts.Constants;
import org.dromara.dbswitch.common.entity.IncrementPoint;
import org.dromara.dbswitch.common.entity.ResultSetWrapper;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import org.dromara.dbswitch.core.provider.ProductFactoryProvider;
@@ -50,7 +51,7 @@ public class ElasticsearchTableDataQueryProvider implements TableDataQueryProvid
@Override
public ResultSetWrapper queryTableData(String schemaName, String tableName, List<String> fields,
List<String> orders) {
IncrementPoint point, List<String> orders) {
String sql = tableName;
try {
Connection connection = this.dataSource.getConnection();

View File

@@ -1,6 +1,7 @@
package org.dromara.dbswitch.product.mongodb;
import org.dromara.dbswitch.common.consts.Constants;
import org.dromara.dbswitch.common.entity.IncrementPoint;
import org.dromara.dbswitch.common.entity.ResultSetWrapper;
import org.dromara.dbswitch.common.type.ProductTypeEnum;
import org.dromara.dbswitch.core.provider.ProductFactoryProvider;
@@ -42,7 +43,7 @@ public class MongodbTableDataQueryProvider implements TableDataQueryProvider {
@Override
public ResultSetWrapper queryTableData(String schemaName, String tableName, List<String> fields,
List<String> orders) {
IncrementPoint point, List<String> orders) {
String sql = String.format("%s.getCollection('%s').find().sort({ %s })",
schemaName, tableName, orders.stream().map(s -> String.format("'%s' : 1", s))
.collect(Collectors.joining(",")));

View File

@@ -291,7 +291,7 @@ public class MysqlMetadataQueryProvider extends AbstractMetadataProvider {
if (length > 65) {
length = 65;
}
if (length > 15) {
if (length >= 15) {
retval += "DECIMAL(" + length;
if (precision > 0) {
if (precision > 30) {