mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-11-30 02:48:17 +08:00
H2 数据库升级
This commit is contained in:
parent
05807718ea
commit
9b6b4316f5
19
CHANGELOG.md
19
CHANGELOG.md
@ -1,5 +1,24 @@
|
||||
# 🚀 版本日志
|
||||
|
||||
# 2.9.0
|
||||
|
||||
### 🐣 新增功能
|
||||
|
||||
### 🐞 解决BUG、优化功能
|
||||
|
||||
1. 【server】升级 h2 版本,低版本存在漏洞(CVE-2021-23463)
|
||||
|
||||
### ⚠️ 注意
|
||||
|
||||
> 此版本为不兼容升级,需要手动升级操作数据相关迁移,操作流程如下:
|
||||
|
||||
1. 导出低版本数据
|
||||
1. 启动程序参数里面添加 --backup-h2
|
||||
2. linux 环境举例:`sh /xxxx/Server.sh restart --backup-h2`
|
||||
2. 将导出的低版本数据( sql 文件) 导入到新版本中
|
||||
1. 启动程序参数里面添加 --replace-import-h2-sql=/xxxx.sql (路径需要替换为第一步控制台输出的 sql 文件保存路径)
|
||||
2. linux 环境举例:`sh /xxxx/Server.sh restart --replace-import-h2-sql=/xxxx.sql`
|
||||
|
||||
# 2.8.24 (2022-06-09)
|
||||
|
||||
### 🐣 新增功能
|
||||
|
@ -32,11 +32,25 @@ import cn.hutool.core.util.StrUtil;
|
||||
*/
|
||||
public class JpomRuntimeException extends RuntimeException {
|
||||
|
||||
public JpomRuntimeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
/**
|
||||
* 程序是否需要关闭
|
||||
*/
|
||||
private Integer exitCode;
|
||||
|
||||
public JpomRuntimeException(String message, Throwable throwable) {
|
||||
super(StrUtil.format("{} {}", message, StrUtil.emptyToDefault(throwable.getMessage(), StrUtil.EMPTY)), throwable);
|
||||
}
|
||||
public JpomRuntimeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JpomRuntimeException(String message, Integer exitCode) {
|
||||
super(message);
|
||||
this.exitCode = exitCode;
|
||||
}
|
||||
|
||||
public JpomRuntimeException(String message, Throwable throwable) {
|
||||
super(StrUtil.format("{} {}", message, StrUtil.emptyToDefault(throwable.getMessage(), StrUtil.EMPTY)), throwable);
|
||||
}
|
||||
|
||||
public Integer getExitCode() {
|
||||
return exitCode;
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +83,8 @@ public class JpomServerApplication implements ApplicationEventLoad {
|
||||
* --backup-h2 备份数据库
|
||||
* <p>
|
||||
* --import-h2-sql=/xxxx.sql 导入指定文件 sql
|
||||
* <p>
|
||||
* --replace-import-h2-sql=/xxxx.sql 替换导入指定文件 sql(会删除掉已经存的数据)
|
||||
*
|
||||
* @param args 参数
|
||||
* @throws Exception 异常
|
||||
@ -162,21 +164,38 @@ public class JpomServerApplication implements ApplicationEventLoad {
|
||||
String importH2Sql = StringUtil.getArgsValue(ARGS, "import-h2-sql");
|
||||
if (StrUtil.isNotEmpty(importH2Sql)) {
|
||||
// 导入数据
|
||||
InitDb.addCallback(() -> {
|
||||
File file = FileUtil.file(importH2Sql);
|
||||
String sqlPath = FileUtil.getAbsolutePath(file);
|
||||
if (!FileUtil.isFile(file)) {
|
||||
consoleExit(2, "sql file does not exist :{}", sqlPath);
|
||||
}
|
||||
Console.log("Start importing data:{}", sqlPath);
|
||||
BackupInfoService backupInfoService = SpringUtil.getBean(BackupInfoService.class);
|
||||
boolean flag = backupInfoService.restoreWithSql(sqlPath);
|
||||
if (!flag) {
|
||||
consoleExit(2, "Failed to import according to sql,{}", sqlPath);
|
||||
}
|
||||
Console.log("Import successfully according to sql,{}", sqlPath);
|
||||
});
|
||||
importH2Sql(importH2Sql);
|
||||
}
|
||||
String replaceImportH2Sql = StringUtil.getArgsValue(ARGS, "replace-import-h2-sql");
|
||||
if (StrUtil.isNotEmpty(replaceImportH2Sql)) {
|
||||
// 删除掉旧数据
|
||||
try {
|
||||
instance.deleteDbFiles();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
consoleExit(-2, "Failed to import according to sql,{}", replaceImportH2Sql);
|
||||
}
|
||||
// 导入数据
|
||||
importH2Sql(replaceImportH2Sql);
|
||||
}
|
||||
//
|
||||
}
|
||||
|
||||
private static void importH2Sql(String importH2Sql) {
|
||||
InitDb.addCallback(() -> {
|
||||
File file = FileUtil.file(importH2Sql);
|
||||
String sqlPath = FileUtil.getAbsolutePath(file);
|
||||
if (!FileUtil.isFile(file)) {
|
||||
consoleExit(2, "sql file does not exist :{}", sqlPath);
|
||||
}
|
||||
Console.log("Start importing data:{}", sqlPath);
|
||||
BackupInfoService backupInfoService = SpringUtil.getBean(BackupInfoService.class);
|
||||
boolean flag = backupInfoService.restoreWithSql(sqlPath);
|
||||
if (!flag) {
|
||||
consoleExit(2, "Failed to import according to sql,{}", sqlPath);
|
||||
}
|
||||
Console.log("Import successfully according to sql,{}", sqlPath);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -41,6 +41,7 @@ import io.jpom.system.db.DbConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.h2.jdbc.JdbcSQLNonTransientConnectionException;
|
||||
import org.h2.jdbc.JdbcSQLNonTransientException;
|
||||
import org.h2.mvstore.MVStoreException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
@ -263,13 +264,13 @@ public abstract class BaseDbCommonService<T> {
|
||||
}
|
||||
Entity where = new Entity(tableName);
|
||||
where.set(key, keyValue);
|
||||
Db db = Db.use();
|
||||
db.setWrapper((Character) null);
|
||||
if (consumer != null) {
|
||||
consumer.accept(where);
|
||||
}
|
||||
Entity entity;
|
||||
try {
|
||||
Db db = Db.use();
|
||||
db.setWrapper((Character) null);
|
||||
if (consumer != null) {
|
||||
consumer.accept(where);
|
||||
}
|
||||
entity = db.get(where);
|
||||
} catch (Exception e) {
|
||||
throw warpException(e);
|
||||
@ -370,9 +371,9 @@ public abstract class BaseDbCommonService<T> {
|
||||
if (where.isEmpty()) {
|
||||
throw new JpomRuntimeException("没有删除条件");
|
||||
}
|
||||
Db db = Db.use();
|
||||
db.setWrapper((Character) null);
|
||||
try {
|
||||
Db db = Db.use();
|
||||
db.setWrapper((Character) null);
|
||||
return db.del(where);
|
||||
} catch (Exception e) {
|
||||
throw warpException(e);
|
||||
@ -674,11 +675,21 @@ public abstract class BaseDbCommonService<T> {
|
||||
* @param e 异常
|
||||
*/
|
||||
protected JpomRuntimeException warpException(Exception e) {
|
||||
String message = e.getMessage();
|
||||
if (e instanceof MVStoreException || ExceptionUtil.isCausedBy(e, MVStoreException.class)) {
|
||||
if (StrUtil.containsIgnoreCase(message, "The write format 1 is smaller than the supported format 2")) {
|
||||
log.warn(message);
|
||||
String tip = "升级数据库流程:" + StrUtil.LF;
|
||||
tip += StrUtil.TAB + "1. 导出低版本数据 【启动程序参数里面添加 --backup-h2】" + StrUtil.LF;
|
||||
tip += StrUtil.TAB + "2. 将导出的低版本数据( sql 文件) 导入到新版本中【启动程序参数里面添加 --replace-import-h2-sql=/xxxx.sql (路径需要替换为第一步控制台输出的 sql 文件保存路径)】";
|
||||
return new JpomRuntimeException("数据库版本不兼容,需要处理跨版本升级。" + StrUtil.LF + tip + StrUtil.LF, -1);
|
||||
}
|
||||
}
|
||||
if (e instanceof JdbcSQLNonTransientException || ExceptionUtil.isCausedBy(e, JdbcSQLNonTransientException.class)) {
|
||||
return new JpomRuntimeException("数据库异常,可能数据库文件已经损坏(可能丢失部分数据),需要重新初始化。可以尝试在启动参数里面添加 --recover:h2db 来自动恢复,:" + e.getMessage(), e);
|
||||
return new JpomRuntimeException("数据库异常,可能数据库文件已经损坏(可能丢失部分数据),需要重新初始化。可以尝试在启动参数里面添加 --recover:h2db 来自动恢复,:" + message, e);
|
||||
}
|
||||
if (e instanceof JdbcSQLNonTransientConnectionException) {
|
||||
return new JpomRuntimeException("数据库异常,可能因为服务器资源不足(内存、硬盘)等原因造成数据异常关闭。需要手动重启服务端来恢复,:" + e.getMessage(), e);
|
||||
return new JpomRuntimeException("数据库异常,可能因为服务器资源不足(内存、硬盘)等原因造成数据异常关闭。需要手动重启服务端来恢复,:" + message, e);
|
||||
}
|
||||
return new JpomRuntimeException("数据库异常", e);
|
||||
}
|
||||
|
@ -45,154 +45,175 @@ import java.util.*;
|
||||
*/
|
||||
public class DbConfig {
|
||||
|
||||
private static final String DB = "db";
|
||||
private static final String DB = "db";
|
||||
|
||||
/**
|
||||
* 默认的账号或者密码
|
||||
*/
|
||||
public static final String DEFAULT_USER_OR_AUTHORIZATION = "jpom";
|
||||
/**
|
||||
* 默认的账号或者密码
|
||||
*/
|
||||
public static final String DEFAULT_USER_OR_AUTHORIZATION = "jpom";
|
||||
|
||||
private static DbConfig dbConfig;
|
||||
private static DbConfig dbConfig;
|
||||
|
||||
/**
|
||||
* 是否初始化成功
|
||||
*/
|
||||
private volatile boolean init;
|
||||
/**
|
||||
* 是否初始化成功
|
||||
*/
|
||||
private volatile boolean init;
|
||||
|
||||
/**
|
||||
* 恢复 sql 文件
|
||||
*/
|
||||
private File recoverSqlFile;
|
||||
/**
|
||||
* 恢复 sql 文件
|
||||
*/
|
||||
private File recoverSqlFile;
|
||||
|
||||
/**
|
||||
* 单利模式
|
||||
*
|
||||
* @return config
|
||||
*/
|
||||
public static DbConfig getInstance() {
|
||||
if (dbConfig == null) {
|
||||
dbConfig = new DbConfig();
|
||||
}
|
||||
return dbConfig;
|
||||
}
|
||||
/**
|
||||
* 单利模式
|
||||
*
|
||||
* @return config
|
||||
*/
|
||||
public static DbConfig getInstance() {
|
||||
if (dbConfig == null) {
|
||||
dbConfig = new DbConfig();
|
||||
}
|
||||
return dbConfig;
|
||||
}
|
||||
|
||||
public void initOk() {
|
||||
init = true;
|
||||
}
|
||||
public void initOk() {
|
||||
init = true;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
init = false;
|
||||
}
|
||||
public void close() {
|
||||
init = false;
|
||||
}
|
||||
|
||||
public boolean isInit() {
|
||||
return init;
|
||||
}
|
||||
public boolean isInit() {
|
||||
return init;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库保存路径
|
||||
*
|
||||
* @return 默认本地数据目录下面的 db 目录
|
||||
* @author bwcx_jzy
|
||||
*/
|
||||
public File dbLocalPath() {
|
||||
return FileUtil.file(ExtConfigBean.getInstance().getPath(), DB);
|
||||
}
|
||||
/**
|
||||
* 获取数据库保存路径
|
||||
*
|
||||
* @return 默认本地数据目录下面的 db 目录
|
||||
* @author bwcx_jzy
|
||||
*/
|
||||
public File dbLocalPath() {
|
||||
return FileUtil.file(ExtConfigBean.getInstance().getPath(), DB);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数据库的jdbc 连接
|
||||
*
|
||||
* @return jdbc
|
||||
*/
|
||||
public String getDbUrl() {
|
||||
DbExtConfig dbExtConfig = SpringUtil.getBean(DbExtConfig.class);
|
||||
String dbUrl = dbExtConfig.getUrl();
|
||||
if (StrUtil.isNotEmpty(dbUrl)) {
|
||||
return dbUrl;
|
||||
}
|
||||
File file = FileUtil.file(this.dbLocalPath(), this.getDbName());
|
||||
String path = FileUtil.getAbsolutePath(file);
|
||||
return StrUtil.format("jdbc:h2:{};CACHE_SIZE={};MODE=MYSQL", path, dbExtConfig.getCacheSize().toKilobytes());
|
||||
}
|
||||
/**
|
||||
* 获取数据库的jdbc 连接
|
||||
*
|
||||
* @return jdbc
|
||||
*/
|
||||
public String getDbUrl() {
|
||||
DbExtConfig dbExtConfig = SpringUtil.getBean(DbExtConfig.class);
|
||||
String dbUrl = dbExtConfig.getUrl();
|
||||
if (StrUtil.isNotEmpty(dbUrl)) {
|
||||
return dbUrl;
|
||||
}
|
||||
File file = FileUtil.file(this.dbLocalPath(), this.getDbName());
|
||||
String path = FileUtil.getAbsolutePath(file);
|
||||
return StrUtil.format("jdbc:h2:{};CACHE_SIZE={};MODE=MYSQL", path, dbExtConfig.getCacheSize().toKilobytes());
|
||||
}
|
||||
|
||||
public String getDbName() {
|
||||
return JpomApplication.getAppType().name();
|
||||
}
|
||||
public String getDbName() {
|
||||
return JpomApplication.getAppType().name();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 本地已经执行的记录
|
||||
*
|
||||
* @return sha1 log
|
||||
* @author bwcx_jzy
|
||||
*/
|
||||
public Set<String> loadExecuteSqlLog() {
|
||||
File localPath = this.dbLocalPath();
|
||||
File file = FileUtil.file(localPath, "execute.init.sql.log");
|
||||
if (!FileUtil.isFile(file)) {
|
||||
// 不存在或者是文件夹
|
||||
FileUtil.del(file);
|
||||
return new LinkedHashSet<>();
|
||||
}
|
||||
List<String> strings = FileUtil.readLines(file, CharsetUtil.CHARSET_UTF_8);
|
||||
return new LinkedHashSet<>(strings);
|
||||
}
|
||||
/**
|
||||
* 加载 本地已经执行的记录
|
||||
*
|
||||
* @return sha1 log
|
||||
* @author bwcx_jzy
|
||||
*/
|
||||
public Set<String> loadExecuteSqlLog() {
|
||||
File localPath = this.dbLocalPath();
|
||||
File file = FileUtil.file(localPath, "execute.init.sql.log");
|
||||
if (!FileUtil.isFile(file)) {
|
||||
// 不存在或者是文件夹
|
||||
FileUtil.del(file);
|
||||
return new LinkedHashSet<>();
|
||||
}
|
||||
List<String> strings = FileUtil.readLines(file, CharsetUtil.CHARSET_UTF_8);
|
||||
return new LinkedHashSet<>(strings);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除执行记录
|
||||
*/
|
||||
public void clearExecuteSqlLog() {
|
||||
File localPath = this.dbLocalPath();
|
||||
File file = FileUtil.file(localPath, "execute.init.sql.log");
|
||||
FileUtil.del(file);
|
||||
}
|
||||
/**
|
||||
* 清除执行记录
|
||||
*/
|
||||
public void clearExecuteSqlLog() {
|
||||
File localPath = this.dbLocalPath();
|
||||
File file = FileUtil.file(localPath, "execute.init.sql.log");
|
||||
FileUtil.del(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复数据库
|
||||
*
|
||||
* @param dsFactory 数据库连接
|
||||
*/
|
||||
public void executeRecoverDbSql(DSFactory dsFactory) throws Exception {
|
||||
if (!FileUtil.isFile(this.recoverSqlFile)) {
|
||||
return;
|
||||
}
|
||||
//
|
||||
IPlugin plugin = PluginFactory.getPlugin("db-h2");
|
||||
Map<String, Object> map = new HashMap<>(10);
|
||||
map.put("backupSqlPath", FileUtil.getAbsolutePath(this.recoverSqlFile));
|
||||
map.put("dataSource", dsFactory.getDataSource());
|
||||
plugin.execute("restoreBackupSql", map);
|
||||
}
|
||||
/**
|
||||
* 恢复数据库
|
||||
*
|
||||
* @param dsFactory 数据库连接
|
||||
*/
|
||||
public void executeRecoverDbSql(DSFactory dsFactory) throws Exception {
|
||||
if (!FileUtil.isFile(this.recoverSqlFile)) {
|
||||
return;
|
||||
}
|
||||
//
|
||||
IPlugin plugin = PluginFactory.getPlugin("db-h2");
|
||||
Map<String, Object> map = new HashMap<>(10);
|
||||
map.put("backupSqlPath", FileUtil.getAbsolutePath(this.recoverSqlFile));
|
||||
map.put("dataSource", dsFactory.getDataSource());
|
||||
plugin.execute("restoreBackupSql", map);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复数据库
|
||||
*/
|
||||
public void recoverDb() throws Exception {
|
||||
File dbLocalPathFile = this.dbLocalPath();
|
||||
if (!FileUtil.exist(dbLocalPathFile)) {
|
||||
return;
|
||||
}
|
||||
String dbName = this.getDbName();
|
||||
File recoverBackup = FileUtil.file(dbLocalPathFile, "recover_backup", DateTime.now().toString());
|
||||
//
|
||||
IPlugin plugin = PluginFactory.getPlugin("db-h2");
|
||||
Map<String, Object> map = new HashMap<>(10);
|
||||
map.put("dbName", dbName);
|
||||
map.put("dbPath", dbLocalPathFile);
|
||||
map.put("recoverBackup", recoverBackup);
|
||||
File backupSql = (File) plugin.execute("recoverToSql", map);
|
||||
// 清空记录
|
||||
this.clearExecuteSqlLog();
|
||||
// 记录恢复的 sql
|
||||
this.recoverSqlFile = backupSql;
|
||||
}
|
||||
/**
|
||||
* 恢复数据库
|
||||
*/
|
||||
public void recoverDb() throws Exception {
|
||||
File dbLocalPathFile = this.dbLocalPath();
|
||||
if (!FileUtil.exist(dbLocalPathFile)) {
|
||||
return;
|
||||
}
|
||||
String dbName = this.getDbName();
|
||||
File recoverBackup = FileUtil.file(dbLocalPathFile, "recover_backup", DateTime.now().toString());
|
||||
//
|
||||
IPlugin plugin = PluginFactory.getPlugin("db-h2");
|
||||
Map<String, Object> map = new HashMap<>(10);
|
||||
map.put("dbName", dbName);
|
||||
map.put("dbPath", dbLocalPathFile);
|
||||
map.put("recoverBackup", recoverBackup);
|
||||
File backupSql = (File) plugin.execute("recoverToSql", map);
|
||||
// 清空记录
|
||||
this.clearExecuteSqlLog();
|
||||
// 记录恢复的 sql
|
||||
this.recoverSqlFile = backupSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存本地已经执行的记录
|
||||
*
|
||||
* @author bwcx_jzy
|
||||
*/
|
||||
public void saveExecuteSqlLog(Set<String> logs) {
|
||||
File localPath = this.dbLocalPath();
|
||||
File file = FileUtil.file(localPath, "execute.init.sql.log");
|
||||
FileUtil.writeUtf8Lines(logs, file);
|
||||
}
|
||||
/**
|
||||
* 恢复数据库
|
||||
*/
|
||||
public void deleteDbFiles() throws Exception {
|
||||
File dbLocalPathFile = this.dbLocalPath();
|
||||
if (!FileUtil.exist(dbLocalPathFile)) {
|
||||
return;
|
||||
}
|
||||
String dbName = this.getDbName();
|
||||
File recoverBackup = FileUtil.file(dbLocalPathFile, "recover_backup", DateTime.now().toString());
|
||||
//
|
||||
IPlugin plugin = PluginFactory.getPlugin("db-h2");
|
||||
Map<String, Object> map = new HashMap<>(10);
|
||||
map.put("dbName", dbName);
|
||||
map.put("dbPath", dbLocalPathFile);
|
||||
map.put("recoverBackup", recoverBackup);
|
||||
plugin.execute("deleteDbFiles", map);
|
||||
// 清空记录
|
||||
this.clearExecuteSqlLog();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存本地已经执行的记录
|
||||
*
|
||||
* @author bwcx_jzy
|
||||
*/
|
||||
public void saveExecuteSqlLog(Set<String> logs) {
|
||||
File localPath = this.dbLocalPath();
|
||||
File file = FileUtil.file(localPath, "execute.init.sql.log");
|
||||
FileUtil.writeUtf8Lines(logs, file);
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,11 @@ import io.jpom.model.data.UserModel;
|
||||
import io.jpom.service.h2db.BaseGroupService;
|
||||
import io.jpom.service.h2db.BaseNodeService;
|
||||
import io.jpom.service.system.WorkspaceService;
|
||||
import io.jpom.system.JpomRuntimeException;
|
||||
import io.jpom.system.ServerExtConfigBean;
|
||||
import io.jpom.system.db.DbConfig;
|
||||
import io.jpom.system.extconf.DbExtConfig;
|
||||
import lombok.Lombok;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.DisposableBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@ -215,6 +217,14 @@ public class InitDb implements DisposableBean, InitializingBean {
|
||||
instance.loadSshInfo();
|
||||
instance.loadMonitorInfo();
|
||||
instance.loadOutgivinInfo();
|
||||
} catch (JpomRuntimeException jpomRuntimeException) {
|
||||
Integer exitCode = jpomRuntimeException.getExitCode();
|
||||
if (exitCode != null) {
|
||||
Console.error(jpomRuntimeException.getMessage());
|
||||
System.exit(exitCode);
|
||||
} else {
|
||||
throw Lombok.sneakyThrow(jpomRuntimeException);
|
||||
}
|
||||
} finally {
|
||||
BaseServerController.removeEmpty();
|
||||
}
|
||||
|
@ -58,218 +58,218 @@ import java.util.stream.Stream;
|
||||
* @since 2021-08-02
|
||||
*/
|
||||
public class LoadBuildJsonToDB {
|
||||
private LoadBuildJsonToDB() {
|
||||
private LoadBuildJsonToDB() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 静态内部类实现单例模式
|
||||
*/
|
||||
public static class LoadBuildJsonToDBHolder {
|
||||
private static final LoadBuildJsonToDB INSTANCE = new LoadBuildJsonToDB();
|
||||
}
|
||||
/**
|
||||
* 静态内部类实现单例模式
|
||||
*/
|
||||
public static class LoadBuildJsonToDBHolder {
|
||||
private static final LoadBuildJsonToDB INSTANCE = new LoadBuildJsonToDB();
|
||||
}
|
||||
|
||||
public static LoadBuildJsonToDB getInstance() {
|
||||
return LoadBuildJsonToDBHolder.INSTANCE;
|
||||
}
|
||||
public static LoadBuildJsonToDB getInstance() {
|
||||
return LoadBuildJsonToDBHolder.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* read build.json file to list
|
||||
* and then use list transfer SQL and execute it
|
||||
*/
|
||||
public void doJsonToSql() {
|
||||
File backupOldData = FileUtil.file(ConfigBean.getInstance().getDataPath(), "backup_old_data");
|
||||
// 读取 build.json 文件内容
|
||||
File file = FileUtil.file(ConfigBean.getInstance().getDataPath(), ServerConfigBean.BUILD);
|
||||
List<JSONObject> list = readBuildJsonFileToList(file);
|
||||
// 判断 list 是否为空
|
||||
if (null == list) {
|
||||
if (!FileUtil.exist(FileUtil.file(backupOldData, ServerConfigBean.BUILD))) {
|
||||
DefaultSystemLog.getLog().warn("There is no any data, the build.json file maybe no content or file is not exist...");
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 转换成 SQL 执行
|
||||
initSql(list);
|
||||
// 将 json 文件转移到备份目录
|
||||
FileUtil.move(file, FileUtil.mkdir(backupOldData), true);
|
||||
DefaultSystemLog.getLog().info("{} mv to {}", FileUtil.getAbsolutePath(file), FileUtil.getAbsolutePath(backupOldData));
|
||||
}
|
||||
/**
|
||||
* read build.json file to list
|
||||
* and then use list transfer SQL and execute it
|
||||
*/
|
||||
public void doJsonToSql() {
|
||||
File backupOldData = FileUtil.file(ConfigBean.getInstance().getDataPath(), "backup_old_data");
|
||||
// 读取 build.json 文件内容
|
||||
File file = FileUtil.file(ConfigBean.getInstance().getDataPath(), ServerConfigBean.BUILD);
|
||||
List<JSONObject> list = readBuildJsonFileToList(file);
|
||||
// 判断 list 是否为空
|
||||
if (null == list) {
|
||||
if (!FileUtil.exist(FileUtil.file(backupOldData, ServerConfigBean.BUILD))) {
|
||||
//DefaultSystemLog.getLog().warn("There is no any data, the build.json file maybe no content or file is not exist...");
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 转换成 SQL 执行
|
||||
initSql(list);
|
||||
// 将 json 文件转移到备份目录
|
||||
FileUtil.move(file, FileUtil.mkdir(backupOldData), true);
|
||||
DefaultSystemLog.getLog().info("{} mv to {}", FileUtil.getAbsolutePath(file), FileUtil.getAbsolutePath(backupOldData));
|
||||
}
|
||||
|
||||
/**
|
||||
* list data to SQL
|
||||
* this method is core logic
|
||||
* 1. load fields will be insert into database from class with reflection
|
||||
* 2. iterate list data, and transfer each element to SQL (element's properties mapping to SQL param name and value)
|
||||
* 3. use param map generate SQL
|
||||
* 4. exec SQL
|
||||
* ---------------------
|
||||
* 这个方法是核心逻辑
|
||||
* 1.通过反射加载字段,将其插入到数据库中
|
||||
* 2. 迭代列表数据,并将每个元素转移到参数集合(元素的属性映射到SQL参数名称和值)
|
||||
* 3. 使用参数集合对象生成SQL
|
||||
* 4. 执行SQL
|
||||
*
|
||||
* @param list data from build.json
|
||||
*/
|
||||
private void initSql(List<JSONObject> list) {
|
||||
// 加载类里面的属性,用反射获取
|
||||
final List<String> repositoryFieldList = getClassFieldList(RepositoryModel.class);
|
||||
final List<String> buildInfoFieldList = getClassFieldList(BuildInfoModel.class);
|
||||
final Map<String, String> repositoryCache = new HashMap<>(list.size());
|
||||
/**
|
||||
* list data to SQL
|
||||
* this method is core logic
|
||||
* 1. load fields will be insert into database from class with reflection
|
||||
* 2. iterate list data, and transfer each element to SQL (element's properties mapping to SQL param name and value)
|
||||
* 3. use param map generate SQL
|
||||
* 4. exec SQL
|
||||
* ---------------------
|
||||
* 这个方法是核心逻辑
|
||||
* 1.通过反射加载字段,将其插入到数据库中
|
||||
* 2. 迭代列表数据,并将每个元素转移到参数集合(元素的属性映射到SQL参数名称和值)
|
||||
* 3. 使用参数集合对象生成SQL
|
||||
* 4. 执行SQL
|
||||
*
|
||||
* @param list data from build.json
|
||||
*/
|
||||
private void initSql(List<JSONObject> list) {
|
||||
// 加载类里面的属性,用反射获取
|
||||
final List<String> repositoryFieldList = getClassFieldList(RepositoryModel.class);
|
||||
final List<String> buildInfoFieldList = getClassFieldList(BuildInfoModel.class);
|
||||
final Map<String, String> repositoryCache = new HashMap<>(list.size());
|
||||
|
||||
// 遍历对象集合
|
||||
list.forEach(buildModelVo -> {
|
||||
DefaultSystemLog.getLog().debug("buildModelVo: {}", JSON.toJSONString(buildModelVo));
|
||||
// 遍历对象集合
|
||||
list.forEach(buildModelVo -> {
|
||||
DefaultSystemLog.getLog().debug("buildModelVo: {}", JSON.toJSONString(buildModelVo));
|
||||
|
||||
// 拿到构造 SQL 的参数
|
||||
String gitUrl = buildModelVo.getString("gitUrl");
|
||||
//buildModelVo.getGitUrl();
|
||||
String repositoryId = repositoryCache.get(gitUrl);
|
||||
if (StrUtil.isEmpty(repositoryId)) {
|
||||
// 先存储仓库信息
|
||||
Map<String, Object> repositoryParamMap = initSqlParamMap(repositoryFieldList, buildModelVo);
|
||||
// add def protocol
|
||||
repositoryParamMap.put("PROTOCOL", GitProtocolEnum.HTTP.getCode());
|
||||
// 构造 insert SQL 语句
|
||||
String insertRepositorySql = initInsertSql(repositoryParamMap, RepositoryModel.class);
|
||||
// 插入数据库
|
||||
insertToDB(insertRepositorySql);
|
||||
repositoryId = (String) repositoryParamMap.get("ID");
|
||||
// cache
|
||||
repositoryCache.put(gitUrl, repositoryId);
|
||||
}
|
||||
// 拿到构造 SQL 的参数
|
||||
String gitUrl = buildModelVo.getString("gitUrl");
|
||||
//buildModelVo.getGitUrl();
|
||||
String repositoryId = repositoryCache.get(gitUrl);
|
||||
if (StrUtil.isEmpty(repositoryId)) {
|
||||
// 先存储仓库信息
|
||||
Map<String, Object> repositoryParamMap = initSqlParamMap(repositoryFieldList, buildModelVo);
|
||||
// add def protocol
|
||||
repositoryParamMap.put("PROTOCOL", GitProtocolEnum.HTTP.getCode());
|
||||
// 构造 insert SQL 语句
|
||||
String insertRepositorySql = initInsertSql(repositoryParamMap, RepositoryModel.class);
|
||||
// 插入数据库
|
||||
insertToDB(insertRepositorySql);
|
||||
repositoryId = (String) repositoryParamMap.get("ID");
|
||||
// cache
|
||||
repositoryCache.put(gitUrl, repositoryId);
|
||||
}
|
||||
|
||||
Map<String, Object> buildInfoParamMap = initSqlParamMap(buildInfoFieldList, buildModelVo);
|
||||
// 绑定仓库ID
|
||||
buildInfoParamMap.put("REPOSITORYID", repositoryId);
|
||||
// 构建发布操作信息
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String releaseMethodDataId = buildModelVo.getString("releaseMethodDataId");
|
||||
jsonObject.put("releaseMethodDataId", releaseMethodDataId);
|
||||
jsonObject.put("afterOpt", buildModelVo.getInteger("afterOpt"));
|
||||
jsonObject.put("clearOld", buildModelVo.getBoolean("clearOld"));
|
||||
jsonObject.put("releaseCommand", buildModelVo.getString("releaseCommand"));
|
||||
jsonObject.put("releasePath", buildModelVo.getString("releasePath"));
|
||||
// 保存信息
|
||||
buildInfoParamMap.put("EXTRADATA", jsonObject.toJSONString());
|
||||
buildInfoParamMap.put("RELEASEMETHODDATAID", releaseMethodDataId);
|
||||
String insertBuildInfoSql = initInsertSql(buildInfoParamMap, BuildInfoModel.class);
|
||||
Map<String, Object> buildInfoParamMap = initSqlParamMap(buildInfoFieldList, buildModelVo);
|
||||
// 绑定仓库ID
|
||||
buildInfoParamMap.put("REPOSITORYID", repositoryId);
|
||||
// 构建发布操作信息
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
String releaseMethodDataId = buildModelVo.getString("releaseMethodDataId");
|
||||
jsonObject.put("releaseMethodDataId", releaseMethodDataId);
|
||||
jsonObject.put("afterOpt", buildModelVo.getInteger("afterOpt"));
|
||||
jsonObject.put("clearOld", buildModelVo.getBoolean("clearOld"));
|
||||
jsonObject.put("releaseCommand", buildModelVo.getString("releaseCommand"));
|
||||
jsonObject.put("releasePath", buildModelVo.getString("releasePath"));
|
||||
// 保存信息
|
||||
buildInfoParamMap.put("EXTRADATA", jsonObject.toJSONString());
|
||||
buildInfoParamMap.put("RELEASEMETHODDATAID", releaseMethodDataId);
|
||||
String insertBuildInfoSql = initInsertSql(buildInfoParamMap, BuildInfoModel.class);
|
||||
|
||||
insertToDB(insertBuildInfoSql);
|
||||
});
|
||||
}
|
||||
insertToDB(insertBuildInfoSql);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* exec insert SQL to DB
|
||||
*
|
||||
* @param sql SQL for insert
|
||||
*/
|
||||
private void insertToDB(String sql) {
|
||||
DSFactory dsFactory = GlobalDSFactory.get();
|
||||
int rows = 0;
|
||||
try {
|
||||
rows = Db.use(dsFactory.getDataSource()).execute(sql);
|
||||
} catch (SQLException e) {
|
||||
DefaultSystemLog.getLog().warn("exec SQL: {} failed", sql, e);
|
||||
}
|
||||
DefaultSystemLog.getLog().info("exec SQL: {} complete, and affected rows is: {}", sql, rows);
|
||||
}
|
||||
/**
|
||||
* exec insert SQL to DB
|
||||
*
|
||||
* @param sql SQL for insert
|
||||
*/
|
||||
private void insertToDB(String sql) {
|
||||
DSFactory dsFactory = GlobalDSFactory.get();
|
||||
int rows = 0;
|
||||
try {
|
||||
rows = Db.use(dsFactory.getDataSource()).execute(sql);
|
||||
} catch (SQLException e) {
|
||||
DefaultSystemLog.getLog().warn("exec SQL: {} failed", sql, e);
|
||||
}
|
||||
DefaultSystemLog.getLog().info("exec SQL: {} complete, and affected rows is: {}", sql, rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* init insert SQL with param map and table name
|
||||
*
|
||||
* @param paramMap
|
||||
* @param clazz 实体类
|
||||
* @return
|
||||
*/
|
||||
private String initInsertSql(Map<String, Object> paramMap, Class<?> clazz) {
|
||||
TableName tableName = clazz.getAnnotation(TableName.class);
|
||||
Assert.notNull(tableName, "not find table name");
|
||||
// 构造 insert SQL 语句
|
||||
StringBuffer sqlBuffer = new StringBuffer("merge into {} ( ");
|
||||
StringBuilder sqlFieldNameBuffer = new StringBuilder();
|
||||
StringBuilder sqlFieldValueBuffer = new StringBuilder();
|
||||
for (int i = 0; i < paramMap.size(); i++) {
|
||||
sqlFieldNameBuffer.append("`{}`,");
|
||||
sqlFieldValueBuffer.append("'{}',");
|
||||
}
|
||||
sqlBuffer.append(sqlFieldNameBuffer.substring(0, sqlFieldNameBuffer.length() - 1))
|
||||
.append(" )")
|
||||
.append(" values ( ")
|
||||
.append(sqlFieldValueBuffer.substring(0, sqlFieldValueBuffer.length() - 1))
|
||||
.append(" )");
|
||||
/**
|
||||
* init insert SQL with param map and table name
|
||||
*
|
||||
* @param paramMap
|
||||
* @param clazz 实体类
|
||||
* @return
|
||||
*/
|
||||
private String initInsertSql(Map<String, Object> paramMap, Class<?> clazz) {
|
||||
TableName tableName = clazz.getAnnotation(TableName.class);
|
||||
Assert.notNull(tableName, "not find table name");
|
||||
// 构造 insert SQL 语句
|
||||
StringBuffer sqlBuffer = new StringBuffer("merge into {} ( ");
|
||||
StringBuilder sqlFieldNameBuffer = new StringBuilder();
|
||||
StringBuilder sqlFieldValueBuffer = new StringBuilder();
|
||||
for (int i = 0; i < paramMap.size(); i++) {
|
||||
sqlFieldNameBuffer.append("`{}`,");
|
||||
sqlFieldValueBuffer.append("'{}',");
|
||||
}
|
||||
sqlBuffer.append(sqlFieldNameBuffer.substring(0, sqlFieldNameBuffer.length() - 1))
|
||||
.append(" )")
|
||||
.append(" values ( ")
|
||||
.append(sqlFieldValueBuffer.substring(0, sqlFieldValueBuffer.length() - 1))
|
||||
.append(" )");
|
||||
|
||||
// 构造 SQL 参数
|
||||
List<Object> params = new ArrayList<>();
|
||||
params.add(tableName.value());
|
||||
params.addAll(paramMap.keySet());
|
||||
params.addAll(paramMap.values());
|
||||
return StrUtil.format(sqlBuffer, params.toArray());
|
||||
}
|
||||
// 构造 SQL 参数
|
||||
List<Object> params = new ArrayList<>();
|
||||
params.add(tableName.value());
|
||||
params.addAll(paramMap.keySet());
|
||||
params.addAll(paramMap.values());
|
||||
return StrUtil.format(sqlBuffer, params.toArray());
|
||||
}
|
||||
|
||||
/**
|
||||
* init param map for create insert SQL
|
||||
*
|
||||
* @param fieldList 字段名 list
|
||||
* @param jsonObject json
|
||||
* @return map key value
|
||||
*/
|
||||
private Map<String, Object> initSqlParamMap(List<String> fieldList, JSONObject jsonObject) {
|
||||
Map<String, Object> map = new HashMap<>(fieldList.size());
|
||||
/**
|
||||
* init param map for create insert SQL
|
||||
*
|
||||
* @param fieldList 字段名 list
|
||||
* @param jsonObject json
|
||||
* @return map key value
|
||||
*/
|
||||
private Map<String, Object> initSqlParamMap(List<String> fieldList, JSONObject jsonObject) {
|
||||
Map<String, Object> map = new HashMap<>(fieldList.size());
|
||||
|
||||
fieldList.forEach(fieldName -> {
|
||||
// 判断类里面是否有这个属性
|
||||
Object filedValue = jsonObject.get(fieldName);
|
||||
if (filedValue == null) {
|
||||
return;
|
||||
}
|
||||
// 添加到参数对象中
|
||||
String sqlFiledName = fieldName.toUpperCase();
|
||||
map.put(sqlFiledName, filedValue);
|
||||
});
|
||||
// 同步数据创建时间
|
||||
String modifyTime = jsonObject.getString("modifyTime");
|
||||
if (StrUtil.isNotEmpty(modifyTime)) {
|
||||
map.put("CREATETIMEMILLIS", DateUtil.parse(modifyTime).getTime());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
fieldList.forEach(fieldName -> {
|
||||
// 判断类里面是否有这个属性
|
||||
Object filedValue = jsonObject.get(fieldName);
|
||||
if (filedValue == null) {
|
||||
return;
|
||||
}
|
||||
// 添加到参数对象中
|
||||
String sqlFiledName = fieldName.toUpperCase();
|
||||
map.put(sqlFiledName, filedValue);
|
||||
});
|
||||
// 同步数据创建时间
|
||||
String modifyTime = jsonObject.getString("modifyTime");
|
||||
if (StrUtil.isNotEmpty(modifyTime)) {
|
||||
map.put("CREATETIMEMILLIS", DateUtil.parse(modifyTime).getTime());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* read build.json file to list
|
||||
*
|
||||
* @return List<BuildModelVo>
|
||||
*/
|
||||
private List<JSONObject> readBuildJsonFileToList(File file) {
|
||||
if (!file.exists()) {
|
||||
DefaultSystemLog.getLog().debug("there is no build.json file...");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 读取 build.json 文件里面的内容,转换成实体对象集合
|
||||
JSONObject jsonObject = (JSONObject) JsonFileUtil.readJson(file.getAbsolutePath());
|
||||
return jsonObject.keySet().stream()
|
||||
.map(jsonObject::get)
|
||||
.flatMap((Function<Object, Stream<JSONObject>>) o -> Stream.of((JSONObject) o))
|
||||
.collect(Collectors.toList());
|
||||
} catch (FileNotFoundException e) {
|
||||
DefaultSystemLog.getLog().error("read build.json file failed...caused: {}...message: {}", e.getCause(), e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* read build.json file to list
|
||||
*
|
||||
* @return List<BuildModelVo>
|
||||
*/
|
||||
private List<JSONObject> readBuildJsonFileToList(File file) {
|
||||
if (!file.exists()) {
|
||||
DefaultSystemLog.getLog().debug("there is no build.json file...");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 读取 build.json 文件里面的内容,转换成实体对象集合
|
||||
JSONObject jsonObject = (JSONObject) JsonFileUtil.readJson(file.getAbsolutePath());
|
||||
return jsonObject.keySet().stream()
|
||||
.map(jsonObject::get)
|
||||
.flatMap((Function<Object, Stream<JSONObject>>) o -> Stream.of((JSONObject) o))
|
||||
.collect(Collectors.toList());
|
||||
} catch (FileNotFoundException e) {
|
||||
DefaultSystemLog.getLog().error("read build.json file failed...caused: {}...message: {}", e.getCause(), e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 clazz 类里面的属性,转换成集合返回
|
||||
*
|
||||
* @param clazz 实体类
|
||||
* @return List<String>
|
||||
*/
|
||||
private List<String> getClassFieldList(Class<?> clazz) {
|
||||
final Field[] fields = ReflectUtil.getFieldsDirectly(clazz, true);
|
||||
return Arrays.stream(fields)
|
||||
.filter(field -> Modifier.isPrivate(field.getModifiers()))
|
||||
.flatMap(field -> Arrays.stream(new String[]{field.getName()}))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
/**
|
||||
* 获取 clazz 类里面的属性,转换成集合返回
|
||||
*
|
||||
* @param clazz 实体类
|
||||
* @return List<String>
|
||||
*/
|
||||
private List<String> getClassFieldList(Class<?> clazz) {
|
||||
final Field[] fields = ReflectUtil.getFieldsDirectly(clazz, true);
|
||||
return Arrays.stream(fields)
|
||||
.filter(field -> Modifier.isPrivate(field.getModifiers()))
|
||||
.flatMap(field -> Arrays.stream(new String[]{field.getName()}))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.ds.DSFactory;
|
||||
import cn.jiangzeyin.common.DefaultSystemLog;
|
||||
import org.h2.store.FileLister;
|
||||
import org.h2.tools.DeleteDbFiles;
|
||||
import org.h2.tools.Recover;
|
||||
import org.h2.tools.RunScript;
|
||||
import org.h2.tools.Shell;
|
||||
@ -77,6 +78,11 @@ public class DefaultDbH2PluginImpl implements IDefaultPlugin {
|
||||
String dbName = (String) parameter.get("dbName");
|
||||
File recoverBackup = (File) parameter.get("recoverBackup");
|
||||
return this.recover(dbPath, dbName, recoverBackup);
|
||||
} else if (StrUtil.equals("deleteDbFiles", method)) {
|
||||
File dbPath = (File) parameter.get("dbPath");
|
||||
String dbName = (String) parameter.get("dbName");
|
||||
File recoverBackup = (File) parameter.get("recoverBackup");
|
||||
this.deleteDbFiles(dbPath, dbName, recoverBackup);
|
||||
} else {
|
||||
throw new IllegalArgumentException("不支持的类型");
|
||||
}
|
||||
@ -111,6 +117,31 @@ public class DefaultDbH2PluginImpl implements IDefaultPlugin {
|
||||
return FileUtil.file(recoverBackup, dbName + ".h2.sql");
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除数据
|
||||
*
|
||||
* @param dbPath 数据库路径
|
||||
* @param dbName 数据库名
|
||||
* @throws SQLException sql
|
||||
*/
|
||||
private void deleteDbFiles(File dbPath, String dbName, File recoverBackup) throws SQLException {
|
||||
String dbLocalPath = FileUtil.getAbsolutePath(dbPath);
|
||||
ArrayList<String> list = FileLister.getDatabaseFiles(dbLocalPath, dbName, true);
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
if (recoverBackup != null) {
|
||||
FileUtil.mkdir(recoverBackup);
|
||||
// 备份数据
|
||||
for (String s : list) {
|
||||
FileUtil.move(FileUtil.file(s), recoverBackup, true);
|
||||
}
|
||||
}
|
||||
// 删除数据
|
||||
DeleteDbFiles deleteDbFiles = new DeleteDbFiles();
|
||||
deleteDbFiles.runTool("-dir", dbLocalPath, "-db", dbName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 备份 SQL
|
||||
*
|
||||
@ -145,11 +176,11 @@ public class DefaultDbH2PluginImpl implements IDefaultPlugin {
|
||||
* - table 表示需要备份的表名称,后面跟多个表名,用英文逗号分割
|
||||
*/
|
||||
String[] params = new String[]{
|
||||
"-url", url,
|
||||
"-user", user,
|
||||
"-password", password,
|
||||
"-driver", "org.h2.Driver",
|
||||
"-sql", sql
|
||||
"-url", url,
|
||||
"-user", user,
|
||||
"-password", password,
|
||||
"-driver", "org.h2.Driver",
|
||||
"-sql", sql
|
||||
};
|
||||
try (FastByteArrayOutputStream arrayOutputStream = new FastByteArrayOutputStream()) {
|
||||
try (PrintStream printStream = new PrintStream(arrayOutputStream)) {
|
||||
|
Loading…
Reference in New Issue
Block a user