H2 数据库升级

This commit is contained in:
bwcx_jzy 2022-06-10 00:24:02 +08:00
parent 05807718ea
commit 9b6b4316f5
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
9 changed files with 492 additions and 367 deletions

View File

@ -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)
### 🐣 新增功能

View File

@ -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;
}
}

View File

@ -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);
});
}
/**

View File

@ -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);
}

View File

@ -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);
}
}

View 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();
}

View File

@ -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());
}
}

View File

@ -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)) {

View File

@ -57,7 +57,7 @@
<spring-boot.version>2.6.3</spring-boot.version>
<common-boot.version>2.2.4</common-boot.version>
<h2.version>1.4.200</h2.version>
<h2.version>2.1.212</h2.version>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
<maven.test.skip>true</maven.test.skip>