背景
项目开发完成上线或投入生产后,会生成大量数据,针对数据安全考虑大多会进行数据的备份,有的人工操作有的自动备份;
我这边根据需求,客户要求将一些表的数据备份到我们这边的服务器上
当然目前我还处于项目熟悉阶段,可能理解的有所偏差
功能描述
可配置两个(或多个,根据需求)不同的数据库,对其中一些表的数据进行数据迁移,并且可以对原表进行标记(是否已迁移等),也可以对某些数据进行修改——比如将创建时间改为当前时间等(根据实际需求);
开放接口,可以按需求由前端或人工调用,对其它表也能进行数据迁移操作;
可配置定时任务和频率,自动执行数据迁移操作;
上代码
目录结构
controller
存放控制器,对外开放访问接口,尽可能只编写主要逻辑,具体实现交给service层
service、mapper
存放服务实现和映射(操作数据库),service负责业务功能的具体实现,需要与数据库交互时在mapper层编写响应的接口
vo
存放自定义JavaBean,根据业务需求为了方便参数的传递而创建的自定义JavaBean
application.yml
启动配置类,同时也存放数据迁移所需的一些配置,如接收、发送者数据库连接信息和定期任务等
properties
存放对配置文件的读取类,使用配置文件保存配置信息,对于使用者来说远比根据需求传入一堆参数更友好
配置信息
server:
port: 9020
# 配置定时任务执行时间(cron表达式)
schedules:
# 目前默认执行频率为每分钟一次
runBatch: 0 0/1 * * * ?
# 数据转移参数
transfer:
# 发送者数据库地址
sendUrl: jdbc:mysql://地址:端口/数据库?useUnicode=true&characterEncoding=utf8
# 发送者数据库用户名
sendName: 用户名
# 发送者数据库密码
sendPassword: 密码
# 发送者数据库查询语句
# tableName为表名全称,shortName为表名简称——非必要不用动
sendSql: select * from tableName where shortName_flag_state != '删除'
# 接收者数据库地址
acceptUrl: jdbc:mysql://地址:端口/数据库?useUnicode=true&characterEncoding=utf8
# 接收者数据库用户名
acceptName: 用户名
# 接收者数据库密码
acceptPassword: 密码
# 需要修改值的列名
# shortName为表名简称——非必要不用动
updateColumn: shortName_flag_state
# 需要修改为的值
updateString: 删除
# 是否开启定时转移任务
# 只有配置值为 true 时开启定时任务
schedule: false
spring:
application:
name: transfer #服务名
# 接收者數據庫信息
datasource:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://地址:端口/数据库?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
username: 用户名
password: 密码
依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
核心代码
TransferController
package com.rongyi.transfer.controller;
import com.rongyi.transfer.properties.ApplicationProperties;
import com.rongyi.transfer.service.TransferService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.sql.*;
@Component
@RestController
@RequestMapping("/transfer")
public class TransferController {
@Autowired
private ApplicationProperties properties;
@Autowired
private TransferService transferService;
// 定时遍历迁移
@Scheduled(cron="${schedules.runBatch}")
public void scheduledMove() throws SQLException {
if (properties.getSchedule() != null && "true".equals(properties.getSchedule())) {
String[] shortNames = {"c01", "i02", "w03"};
for (String shortName : shortNames) {
moveTable(shortName);
}
System.out.println("本次定时任务执行完毕");
}
}
// 迁移
@GetMapping("/moveTable/{shortName}")
public String moveTable(@PathVariable("shortName") String shortName) throws SQLException {
transferService.moveCustom(shortName);
return shortName + "迁移完成";
}
}
TransferService
package com.rongyi.transfer.service;
import com.rongyi.transfer.mapper.TransferMapper;
import com.rongyi.transfer.properties.ApplicationProperties;
import com.rongyi.transfer.vo.InsertVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.sql.*;
@Service
public class TransferService {
@Resource
private TransferMapper transferMapper;
@Autowired
private ApplicationProperties properties;
public void moveData(String shortName) throws SQLException {
// 创建变量保存表名
String tableName = "";
// 表名简称不为空时,通过表名简称前缀确定表名
if (shortName == null || "".equals(shortName)) {
// 表名简称为空直接结束
System.out.println("表名不能为空");
return;
}
// 根据简称前缀拼接出全表名
if (shortName.contains("c")) {
tableName = shortName.replace("c", "ctlm");
} else if (shortName.contains("i")) {
tableName = shortName.replace("i", "inv");
} else if (shortName.contains("w")) {
tableName = shortName.replace("w", "weight");
}
// 表名不为空时才可进行数据转移操作
if (tableName == null || "".equals(tableName)) {
// 表名为空说明表名简称前缀无法识别
System.out.println("无法识别表");
return;
} else {
move(tableName, shortName);
}
// 正常执行完成打印成功消息
System.out.println(shortName + "数据转移完毕");
}
private void move(String tableName, String shortName) throws SQLException {
// 建立发送者连接
Connection conSend = DriverManager.getConnection(properties.getSendUrl(), properties.getSendName(), properties.getSendPassword());
Statement stmtSend = conSend.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
// 拿到查询sql
String sql = properties.getSendSql();
// 拼接——替换掉配置中的tableName和shortName
sql = sql.replace("tableName", tableName);
sql = sql.replace("shortName", shortName);
try {
// 通过拼接好的sql查询数据并保存到结果集
ResultSet send = stmtSend.executeQuery(sql);
if (send == null) {
// 结果集为空说明没有新数据,不进行后续操作
System.out.println("没有需要进行迁移的数据");
return;
}
// 建立接收者连接
Connection conAccept = DriverManager.getConnection(properties.getAcceptUrl(), properties.getAcceptName(), properties.getAcceptPassword());
Statement stmtAccept = conAccept.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
// 定位接收表
ResultSet accept = stmtAccept.executeQuery("select * from " + tableName);
// 遍历发送者结果集
while (send.next()) {
// 开启新增
accept.moveToInsertRow();
// 获取当前表结构
ResultSetMetaData metaData = send.getMetaData();
// 通过表结构字段数遍历并设置当前行数据
for (int i = 1; i <= metaData.getColumnCount(); i++) {
accept.updateString(metaData.getColumnLabel(i), send.getString(i));
}
// 保存新增内容
accept.insertRow();
// 创建column保存需要修改的字段名
String column = properties.getUpdateColumn();
column = column.replace("shortName", shortName);
// 修改发送者表需要的字段数据
send.updateString(column, properties.getUpdateString());
send.updateRow();
}
// 有异常时打印异常信息
} catch (SQLException e) {
System.out.println(e.getMessage());
}
}
}
End