mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-11-30 02:48:17 +08:00
增加postgresql数据库支持和h2迁移postgresql功能
This commit is contained in:
parent
5d3b783468
commit
a1c513e058
@ -122,6 +122,12 @@
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.jpom.storage-module</groupId>
|
||||
<artifactId>storage-module-postgresql</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>me.zhyd.oauth</groupId>
|
||||
<artifactId>JustAuth</artifactId>
|
||||
|
@ -26,6 +26,7 @@ import org.dromara.jpom.JpomApplication;
|
||||
import org.dromara.jpom.common.ILoadEvent;
|
||||
import org.dromara.jpom.common.JpomApplicationEvent;
|
||||
import org.dromara.jpom.db.*;
|
||||
import org.dromara.jpom.dialect.DialectUtil;
|
||||
import org.dromara.jpom.model.data.BackupInfoModel;
|
||||
import org.dromara.jpom.service.dblog.BackupInfoService;
|
||||
import org.dromara.jpom.system.JpomRuntimeException;
|
||||
@ -203,7 +204,7 @@ public class InitDb implements DisposableBean, ILoadEvent {
|
||||
eachSql.accept(name);
|
||||
try {
|
||||
IStorageSqlBuilderService sqlBuilderService = StorageTableFactory.get();
|
||||
Db.use(dataSource).tx((CheckedUtil.VoidFunc1Rt<Db>) parameter -> {
|
||||
Db.use(dataSource, DialectUtil.getDialectByMode(mode)).tx((CheckedUtil.VoidFunc1Rt<Db>) parameter -> {
|
||||
// 分隔后执行,mysql 不能执行多条 sql 语句
|
||||
List<String> list = StrUtil.isEmpty(sqlBuilderService.delimiter()) ?
|
||||
CollUtil.newArrayList(sql) : StrUtil.splitTrim(sql, sqlBuilderService.delimiter());
|
||||
@ -279,11 +280,12 @@ public class InitDb implements DisposableBean, ILoadEvent {
|
||||
// 导入数据
|
||||
importH2Sql(environment, s);
|
||||
});
|
||||
|
||||
// 迁移数据
|
||||
Opt.ofNullable(environment.getProperty("h2-migrate-mysql")).ifPresent(s -> {
|
||||
Consumer<DbExtConfig.Mode> migrateOpr = targetMode->{
|
||||
DbExtConfig.Mode mode = dbExtConfig.getMode();
|
||||
if (mode != DbExtConfig.Mode.MYSQL) {
|
||||
throw new JpomRuntimeException(StrUtil.format("当前模式不正确,不能直接迁移到 {}", mode));
|
||||
if (mode != targetMode) {
|
||||
throw new JpomRuntimeException(StrUtil.format("当前模式不正确,不能直接迁移到 {}", targetMode));
|
||||
}
|
||||
// 都提前清理
|
||||
StorageServiceFactory.clearExecuteSqlLog();
|
||||
@ -293,10 +295,16 @@ public class InitDb implements DisposableBean, ILoadEvent {
|
||||
String pass = environment.getProperty("h2-pass");
|
||||
this.addCallback("迁移数据", () -> {
|
||||
//
|
||||
StorageServiceFactory.migrateH2ToNow(dbExtConfig, url, user, pass);
|
||||
StorageServiceFactory.migrateH2ToNow(dbExtConfig, url, user, pass,targetMode);
|
||||
return false;
|
||||
});
|
||||
log.info("开始等待数据迁移");
|
||||
};
|
||||
Opt.ofNullable(environment.getProperty("h2-migrate-mysql")).ifPresent(s -> {
|
||||
migrateOpr.accept(DbExtConfig.Mode.MYSQL);
|
||||
});
|
||||
Opt.ofNullable(environment.getProperty("h2-migrate-postgresql")).ifPresent(s -> {
|
||||
migrateOpr.accept(DbExtConfig.Mode.POSTGRESQL);
|
||||
});
|
||||
}
|
||||
|
||||
|
11
modules/server/src/main/resources/application-postgresql.yml
Normal file
11
modules/server/src/main/resources/application-postgresql.yml
Normal file
@ -0,0 +1,11 @@
|
||||
jpom:
|
||||
db:
|
||||
# 数据库默认 支持 :H2、MYSQL
|
||||
mode: POSTGRESQL
|
||||
url: jdbc:postgresql://localhost:5432/jpom
|
||||
# 数据库账号 默认 jpom
|
||||
#user-name: jpom
|
||||
user-name: jpom
|
||||
# 数据库密码 默认 jpom 如果自行配置请保证密码强度
|
||||
# user-pwd: jpom
|
||||
user-pwd: 123456
|
@ -2,7 +2,7 @@ alterType,tableName,name,type,len,defaultValue,comment,notNull
|
||||
DROP,NODE_INFO,cycle
|
||||
ADD,PROJECT_INFO,triggerToken,String,100,,触发器token
|
||||
ADD,OUT_GIVING,secondaryDirectory,String,200,,二级目录
|
||||
ADD,OUT_GIVING,uploadCloseFirst,TINYINT,,0,是否清空旧包发布
|
||||
ADD,OUT_GIVING,uploadCloseFirst,Boolean,,0,是否清空旧包发布
|
||||
ADD,USEROPERATELOGV1,dataName,String,200,,数据名称
|
||||
DROP,SSHTERMINALEXECUTELOG,optTime
|
||||
DROP,USEROPERATELOGV1,optType
|
||||
|
|
@ -6,7 +6,7 @@ ADD,BUILDHISTORYLOG,buildEnvCache,TEXT,,,构建环境变量
|
||||
ADD,OUT_GIVING,webhook,String,255,,webhook
|
||||
DROP,NODE_INFO,unLockType
|
||||
ADD,NODE_INFO,machineId,String,50,,机器id
|
||||
ADD,MACHINE_NODE_INFO,templateNode,TINYINT,,,模板节点 ,1 模板节点 0 非模板节点
|
||||
ADD,MACHINE_NODE_INFO,templateNode,Boolean,,,模板节点 ,1 模板节点 0 非模板节点
|
||||
ADD,REPOSITORY,timeout,Integer,,,仓库超时连接
|
||||
ADD,SSH_INFO,group,String,50,,分组
|
||||
ADD,SSH_INFO,machineSshId,String,50,,机器sshid
|
||||
@ -28,4 +28,4 @@ DROP,DOCKER_SWARM_INFO,status,
|
||||
DROP,DOCKER_SWARM_INFO,failureMsg,
|
||||
DROP,DOCKER_SWARM_INFO,dockerId,
|
||||
DROP,DOCKER_SWARM_INFO,dockerId,
|
||||
ADD,MACHINE_DOCKER_INFO,swarmControlAvailable,TINYINT,,,集群管理员
|
||||
ADD,MACHINE_DOCKER_INFO,swarmControlAvailable,Boolean,,,集群管理员
|
||||
|
|
@ -40,10 +40,10 @@ ADD,BUILD_INFO,aliasCode,String,50,,别名码,false
|
||||
ADD,FILE_STORAGE,aliasCode,String,50,,别名码,false
|
||||
ADD,PROJECT_INFO,dslContent,TEXT,,,dslContent,false
|
||||
DROP,PROJECT_INFO,jdkId,
|
||||
ADD,PROJECT_INFO,autoStart,TINYINT,,,在启动,false
|
||||
ADD,PROJECT_INFO,autoStart,Boolean,,,在启动,false
|
||||
ADD,MACHINE_DOCKER_INFO,certInfo,String,100,,证书信息,false
|
||||
ADD,CERTIFICATE_INFO,fingerprint,String,50,,证书指纹,false
|
||||
ADD,MACHINE_DOCKER_INFO,certExist,TINYINT,,,证书是否存在,false
|
||||
ADD,MACHINE_DOCKER_INFO,certExist,Boolean,,,证书是否存在,false
|
||||
ADD,REPOSITORY,createUser,String,50,,创建人,false
|
||||
ADD,SERVER_SCRIPT_INFO,createUser,String,50,,创建人,false
|
||||
ADD,BUILD_INFO,statusMsg,TEXT,,,状态信息
|
||||
@ -58,7 +58,7 @@ ADD,SCRIPT_EXECUTE_LOG,workspaceName,String,50,,工作空间名称,false
|
||||
ALTER,REPOSITORY,password,String,255,,登录密码,false
|
||||
ADD,WORKSPACE,group,String,50,,分组,false
|
||||
ADD,MACHINE_SSH_INFO,dockerInfo,String,255,,服务器中的 docker 信息,false
|
||||
ADD,MACHINE_DOCKER_INFO,enableSsh,tinyint,0,,是否开启SSH连接,false
|
||||
ADD,MACHINE_DOCKER_INFO,enableSsh,Boolean,0,,是否开启SSH连接,false
|
||||
ADD,MACHINE_DOCKER_INFO,machineSshId,String,255,,SSH连接信息,false
|
||||
ADD,BUILD_INFO,resultKeepDay,Integer,,,产物保留天数,false
|
||||
ADD,REPOSITORY,group,String,50,,分组,false
|
||||
|
|
@ -0,0 +1,126 @@
|
||||
--
|
||||
-- Copyright (c) 2019 Of Him Code Technology Studio
|
||||
-- Jpom is licensed under Mulan PSL v2.
|
||||
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
-- You may obtain a copy of Mulan PSL v2 at:
|
||||
-- http://license.coscl.org.cn/MulanPSL2
|
||||
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
-- See the Mulan PSL v2 for more details.
|
||||
--
|
||||
|
||||
DROP FUNCTION IF EXISTS column_exists;
|
||||
CREATE FUNCTION column_exists(tname varchar, cname varchar)
|
||||
RETURNS boolean
|
||||
AS
|
||||
$$
|
||||
BEGIN
|
||||
RETURN EXISTS (
|
||||
SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_name = tname
|
||||
AND column_name = cname
|
||||
);
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
-- postgresql $delimiter$
|
||||
|
||||
DROP PROCEDURE IF EXISTS drop_column_if_exists;
|
||||
CREATE PROCEDURE drop_column_if_exists(
|
||||
tname varchar,
|
||||
cname varchar
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
drop_query varchar;
|
||||
BEGIN
|
||||
-- 检查列是否存在
|
||||
IF (select column_exists(tname,cname)) THEN
|
||||
-- 构造ALTER TABLE语句
|
||||
drop_query := format('ALTER TABLE %s DROP COLUMN %s', tname, cname);
|
||||
-- 执行ALTER TABLE语句
|
||||
EXECUTE drop_query;
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- postgresql $delimiter$
|
||||
|
||||
DROP PROCEDURE IF EXISTS add_column_if_not_exists;
|
||||
CREATE PROCEDURE add_column_if_not_exists(
|
||||
tname varchar,
|
||||
cname varchar,
|
||||
columninfo varchar
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
IF NOT (
|
||||
select column_exists(tname,cname)
|
||||
) THEN
|
||||
-- 构造并执行ALTER TABLE语句来添加新列
|
||||
EXECUTE format('ALTER TABLE %s ADD COLUMN %s ', tname, columninfo);
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- postgresql $delimiter$
|
||||
|
||||
DROP PROCEDURE IF EXISTS drop_index_if_exists;
|
||||
CREATE PROCEDURE drop_index_if_exists(
|
||||
p_tablename varchar,
|
||||
p_idxname varchar
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
idx_exists boolean;
|
||||
drop_idx_sql text;
|
||||
BEGIN
|
||||
-- 检查索引是否存在
|
||||
SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_indexes
|
||||
WHERE tablename = p_tablename
|
||||
AND indexname = p_idxname
|
||||
) INTO idx_exists;
|
||||
|
||||
-- 如果索引存在,则构建DROP INDEX语句并执行
|
||||
IF idx_exists THEN
|
||||
drop_idx_sql := format('DROP INDEX IF EXISTS %s', p_idxname);
|
||||
EXECUTE drop_idx_sql;
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- postgresql $delimiter$
|
||||
|
||||
DROP PROCEDURE IF EXISTS exec_if_column_exists;
|
||||
CREATE PROCEDURE exec_if_column_exists(
|
||||
tname varchar,
|
||||
cname varchar,
|
||||
statemetStr varchar
|
||||
)
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
BEGIN
|
||||
IF (
|
||||
select column_exists(tname,cname)
|
||||
) THEN
|
||||
EXECUTE statemetStr;
|
||||
END IF;
|
||||
END;
|
||||
$$;
|
||||
|
||||
-- postgresql $delimiter$
|
||||
|
||||
-- 实现 instr函数,这个是postgresql上没有的
|
||||
DROP FUNCTION IF EXISTS instr;
|
||||
CREATE FUNCTION instr(str1 text, str2 text)
|
||||
RETURNS boolean AS
|
||||
$$
|
||||
SELECT POSITION(str2 IN str1) > 0;
|
||||
$$
|
||||
LANGUAGE sql;
|
@ -76,9 +76,9 @@ MONITOR_INFO,modifyTimeMillis,Long,,,false,false,数据修改时间,
|
||||
MONITOR_INFO,modifyUser,String,50,,false,false,修改人,
|
||||
MONITOR_INFO,workspaceId,String,50,,true,false,所属工作空间,
|
||||
MONITOR_INFO,name,String,50,,true,false,名称,
|
||||
MONITOR_INFO,autoRestart,TINYINT,,0,false,false,是否自动重启{1,是,0 否},
|
||||
MONITOR_INFO,status,TINYINT,,0,false,false,启用状态{1,启用,0 未启用},
|
||||
MONITOR_INFO,alarm,TINYINT,,0,false,false,报警状态{1,报警中,0 未报警},
|
||||
MONITOR_INFO,autoRestart,Boolean,,0,false,false,是否自动重启{1,是,0 否},
|
||||
MONITOR_INFO,status,Boolean,,0,false,false,启用状态{1,启用,0 未启用},
|
||||
MONITOR_INFO,alarm,Boolean,,0,false,false,报警状态{1,报警中,0 未报警},
|
||||
MONITOR_INFO,cycle,Integer,,0,false,false,监控周期,
|
||||
MONITOR_INFO,notifyUser,TEXT,,,false,false,报警联系人,
|
||||
MONITOR_INFO,projects,TEXT,,,false,false,监控的项目,
|
||||
@ -91,7 +91,7 @@ DOCKER_INFO,modifyUser,String,50,,false,false,修改人,
|
||||
DOCKER_INFO,workspaceId,String,50,,true,false,所属工作空间,
|
||||
DOCKER_INFO,name,String,255,,true,false,名称,
|
||||
DOCKER_INFO,host,String,255,,false,false,docker host,
|
||||
DOCKER_INFO,tlsVerify,TINYINT,,0,false,false,tls 认证{1,启用,0 未启用},
|
||||
DOCKER_INFO,tlsVerify,Boolean,,0,false,false,tls 认证{1,启用,0 未启用},
|
||||
DOCKER_INFO,status,TINYINT,,0,false,false,状态{1,启用,0 未启用},
|
||||
DOCKER_INFO,failureMsg,String,255,,false,false,错误消息,
|
||||
DOCKER_INFO,heartbeatTimeout,Integer,,,false,false,心跳超时时间,
|
||||
@ -149,9 +149,9 @@ MONITORNOTIFYLOG,projectId,String,30,,false,false,项目id,
|
||||
MONITORNOTIFYLOG,createTime,Long,,,false,false,异常时间,
|
||||
MONITORNOTIFYLOG,title,String,100,,false,false,异常描述,
|
||||
MONITORNOTIFYLOG,content,TEXT,,,false,false,异常内容,
|
||||
MONITORNOTIFYLOG,status,TINYINT,,,false,false,当前状态,
|
||||
MONITORNOTIFYLOG,status,Boolean,,,false,false,当前状态,
|
||||
MONITORNOTIFYLOG,notifyStyle,TINYINT,,,false,false,通知方式,
|
||||
MONITORNOTIFYLOG,notifyStatus,TINYINT,,,false,false,通知状态,
|
||||
MONITORNOTIFYLOG,notifyStatus,Boolean,,,false,false,通知状态,
|
||||
MONITORNOTIFYLOG,notifyObject,TEXT,,,false,false,通知对象,
|
||||
MONITORNOTIFYLOG,notifyError,TEXT,,,false,false,通知异常内容,
|
||||
MONITORNOTIFYLOG,workspaceId,String,50,,false,false,所属工作空间,
|
||||
@ -187,7 +187,7 @@ MONITOR_USER_OPT,modifyUser,String,50,,false,false,修改人,
|
||||
MONITOR_USER_OPT,workspaceId,String,50,,true,false,所属工作空间,
|
||||
MONITOR_USER_OPT,name,String,50,,true,false,名称,
|
||||
MONITOR_USER_OPT,monitorUser,TEXT,,,false,false,监控的人,
|
||||
MONITOR_USER_OPT,status,TINYINT,,0,false,false,启用状态{1,启用,0 未启用},
|
||||
MONITOR_USER_OPT,status,Boolean,,0,false,false,启用状态{1,启用,0 未启用},
|
||||
MONITOR_USER_OPT,notifyUser,TEXT,,,false,false,报警联系人,
|
||||
MONITOR_USER_OPT,monitorFeature,TEXT,,,false,false,监控的项目,
|
||||
MONITOR_USER_OPT,monitorOpt,TEXT,,,false,false,监控的项目,
|
||||
@ -227,7 +227,7 @@ PROJECT_INFO,args,TEXT,,,false,false,args,
|
||||
PROJECT_INFO,javaCopyItemList,TEXT,,,false,false,javaCopyItemList,
|
||||
PROJECT_INFO,token,String,255,,false,false,token,
|
||||
PROJECT_INFO,runMode,String,20,,false,false,连接方式,
|
||||
PROJECT_INFO,outGivingProject,TINYINT,,0,false,false,分发项目{1,分发,0 独立项目},
|
||||
PROJECT_INFO,outGivingProject,Boolean,,0,false,false,分发项目{1,分发,0 独立项目},
|
||||
PROJECT_INFO,javaExtDirsCp,TEXT,,,false,false,javaExtDirsCp,
|
||||
PROJECT_INFO,sortValue,Float,,,false,false,排序值,
|
||||
PROJECT_INFO,triggerToken,String,100,,false,false,触发器token,
|
||||
@ -297,13 +297,13 @@ OUT_GIVING,modifyUser,String,50,,false,false,修改人,
|
||||
OUT_GIVING,workspaceId,String,50,,true,false,所属工作空间,
|
||||
OUT_GIVING,name,String,50,,true,false,名称,
|
||||
OUT_GIVING,afterOpt,Integer,,0,false,false,分发后的操作,
|
||||
OUT_GIVING,clearOld,TINYINT,,0,false,false,是否清空旧包发布,
|
||||
OUT_GIVING,outGivingProject,TINYINT,,0,false,false,是否为单独创建的分发项目,
|
||||
OUT_GIVING,clearOld,Boolean,,0,false,false,是否清空旧包发布,
|
||||
OUT_GIVING,outGivingProject,Boolean,,0,false,false,是否为单独创建的分发项目,
|
||||
OUT_GIVING,outGivingNodeProjectList,TEXT,,,false,false,分发项目信息,
|
||||
OUT_GIVING,intervalTime,Integer,,10,false,false,分发间隔时间,
|
||||
OUT_GIVING,status,Integer,,0,false,false,状态{0: 未分发; 1: 分发中; 2: 分发结束},
|
||||
OUT_GIVING,secondaryDirectory,String,200,,false,false,二级目录,
|
||||
OUT_GIVING,uploadCloseFirst,TINYINT,,0,false,false,是否清空旧包发布,
|
||||
OUT_GIVING,uploadCloseFirst,Boolean,,0,false,false,是否清空旧包发布,
|
||||
USEROPERATELOGV1,id,String,50,,true,true,id,操作日志
|
||||
USEROPERATELOGV1,ip,String,80,,false,false,客户端IP地址,
|
||||
USEROPERATELOGV1,userId,String,30,,false,false,操作的用户ID,
|
||||
|
|
@ -72,7 +72,7 @@ MACHINE_DOCKER_INFO,modifyUser,String,50,,false,false,修改人,
|
||||
MACHINE_DOCKER_INFO,groupName,String,50,,true,false,分组名称,
|
||||
MACHINE_DOCKER_INFO,name,String,255,,true,false,名称,
|
||||
MACHINE_DOCKER_INFO,host,String,255,,false,false,docker host,
|
||||
MACHINE_DOCKER_INFO,tlsVerify,TINYINT,,0,false,false,tls 认证{1,启用,0 未启用},
|
||||
MACHINE_DOCKER_INFO,tlsVerify,Boolean,,0,false,false,tls 认证{1,启用,0 未启用},
|
||||
MACHINE_DOCKER_INFO,status,TINYINT,,0,false,false,状态{1,启用,0 未启用},
|
||||
MACHINE_DOCKER_INFO,failureMsg,String,255,,false,false,错误消息,
|
||||
MACHINE_DOCKER_INFO,heartbeatTimeout,Integer,,,false,false,心跳超时时间,
|
||||
@ -90,8 +90,8 @@ USER_LOGIN_LOG,modifyTimeMillis,Long,,,false,false,数据修改时间,
|
||||
USER_LOGIN_LOG,modifyUser,String,50,,false,false,修改人,
|
||||
USER_LOGIN_LOG,ip,String,80,,false,false,客户端IP地址,
|
||||
USER_LOGIN_LOG,userAgent,TEXT,,,false,false,浏览器标识,
|
||||
USER_LOGIN_LOG,useMfa,TINYINT,,,false,false,是否使用 mfa,
|
||||
USER_LOGIN_LOG,success,TINYINT,,,false,false,是否登录成功,
|
||||
USER_LOGIN_LOG,useMfa,Boolean,,,false,false,是否使用 mfa,
|
||||
USER_LOGIN_LOG,success,Boolean,,,false,false,是否登录成功,
|
||||
USER_LOGIN_LOG,operateCode,TINYINT,,,false,false,操作状态码(备注码),
|
||||
USER_LOGIN_LOG,username,String,50,,false,false,昵称,
|
||||
FILE_STORAGE,id,String,50,,true,true,id,文件管理中心
|
||||
|
|
@ -24,6 +24,7 @@
|
||||
<module>storage-module-common</module>
|
||||
<module>storage-module-h2</module>
|
||||
<module>storage-module-mysql</module>
|
||||
<module>storage-module-postgresql</module>
|
||||
</modules>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<version>2.11.3</version>
|
||||
|
@ -143,6 +143,10 @@ public class DbExtConfig implements InitializingBean {
|
||||
/**
|
||||
* mysql
|
||||
*/
|
||||
MYSQL
|
||||
MYSQL,
|
||||
/**
|
||||
* postgresql
|
||||
*/
|
||||
POSTGRESQL
|
||||
}
|
||||
}
|
||||
|
@ -22,19 +22,24 @@ import cn.hutool.db.Entity;
|
||||
import cn.hutool.db.Page;
|
||||
import cn.hutool.db.PageResult;
|
||||
import cn.hutool.db.ds.DSFactory;
|
||||
import cn.hutool.db.sql.Wrapper;
|
||||
import cn.hutool.setting.Setting;
|
||||
import lombok.Lombok;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.dialect.DialectUtil;
|
||||
import org.dromara.jpom.system.ExtConfigBean;
|
||||
import org.dromara.jpom.system.JpomRuntimeException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Field;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 数据存储服务
|
||||
@ -67,8 +72,9 @@ public class StorageServiceFactory {
|
||||
/**
|
||||
* 将数据迁移到当前环境
|
||||
*/
|
||||
public static void migrateH2ToNow(DbExtConfig dbExtConfig, String h2Url, String h2User, String h2Pass) {
|
||||
public static void migrateH2ToNow(DbExtConfig dbExtConfig, String h2Url, String h2User, String h2Pass,DbExtConfig.Mode targetNode) {
|
||||
log.info("开始迁移 h2 数据到 {}", dbExtConfig.getMode());
|
||||
Assert.notNull(mode,"未指定目标数据库信息");
|
||||
try {
|
||||
IStorageService h2StorageService = doCreateStorageService(DbExtConfig.Mode.H2);
|
||||
boolean hasDbData = h2StorageService.hasDbData();
|
||||
@ -110,7 +116,7 @@ public class StorageServiceFactory {
|
||||
log.info("准备迁移数据");
|
||||
int total = 0;
|
||||
for (Class<?> aClass : classes) {
|
||||
total += migrateH2ToNowItem(aClass, h2DsFactory, nowDsFactory);
|
||||
total += migrateH2ToNowItem(aClass, h2DsFactory, nowDsFactory,targetNode);
|
||||
}
|
||||
long endTime = SystemClock.now();
|
||||
log.info("迁移完成,累计迁移 {} 条数据,耗时:{}", total, DateUtil.formatBetween(endTime - time));
|
||||
@ -124,14 +130,20 @@ public class StorageServiceFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private static int migrateH2ToNowItem(Class<?> aClass, DSFactory h2DsFactory, DSFactory mysqlDsFactory) throws SQLException {
|
||||
private static int migrateH2ToNowItem(Class<?> aClass, DSFactory h2DsFactory, DSFactory targetDsFactory,DbExtConfig.Mode targetNode) throws SQLException {
|
||||
TableName tableName = aClass.getAnnotation(TableName.class);
|
||||
Wrapper h2FieldWrapper = DialectUtil.getH2Dialect().getWrapper();
|
||||
Set<String> boolFieldSet = Arrays.stream(ReflectUtil.getFields(aClass, field -> Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())))
|
||||
.map(Field::getName)
|
||||
.flatMap(name-> Stream.of(name,h2FieldWrapper.wrap(name)))
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
log.info("开始迁移 {} {}", tableName.name(), tableName.value());
|
||||
int total = 0;
|
||||
while (true) {
|
||||
Entity where = Entity.create(tableName.value());
|
||||
PageResult<Entity> pageResult;
|
||||
Db db = Db.use(h2DsFactory.getDataSource());
|
||||
Db db = Db.use(h2DsFactory.getDataSource(),DialectUtil.getH2Dialect());
|
||||
Page page = new Page(1, 200);
|
||||
pageResult = db.page(where, page);
|
||||
if (pageResult.isEmpty()) {
|
||||
@ -139,12 +151,22 @@ public class StorageServiceFactory {
|
||||
}
|
||||
// 过滤需要忽略迁移的数据
|
||||
List<Entity> newResult = pageResult.stream()
|
||||
.map(entity -> entity.toBeanIgnoreCase(aClass))
|
||||
.map(o -> {
|
||||
// 兼容大小写
|
||||
Entity entity = Entity.create(tableName.value());
|
||||
return entity.parseBean(o, false, true);
|
||||
}).collect(Collectors.toList());
|
||||
.map(entity -> entity.toBeanIgnoreCase(aClass))
|
||||
.map(o -> {
|
||||
// 兼容大小写
|
||||
Entity entity = Entity.create(tableName.value());
|
||||
return entity.parseBean(o, false, true);
|
||||
}).peek(entity -> {
|
||||
if( DbExtConfig.Mode.POSTGRESQL.equals(targetNode) ) {
|
||||
// tinyint类型查出来是数字,需转为bool
|
||||
boolFieldSet.forEach(fieldName->{
|
||||
Object field = entity.get(fieldName);
|
||||
if( field instanceof Number ) {
|
||||
entity.set(fieldName,BooleanUtil.toBoolean(field.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
if (newResult.isEmpty()) {
|
||||
if (pageResult.isLast()) {
|
||||
// 最后一页
|
||||
@ -155,7 +177,7 @@ public class StorageServiceFactory {
|
||||
}
|
||||
total += newResult.size();
|
||||
// 插入信息数据
|
||||
Db db2 = Db.use(mysqlDsFactory.getDataSource());
|
||||
Db db2 = Db.use(targetDsFactory.getDataSource(),DialectUtil.getDialectByMode(targetNode));
|
||||
db2.insert(newResult);
|
||||
// 删除数据
|
||||
Entity deleteWhere = Entity.create(tableName.value());
|
||||
|
@ -0,0 +1,102 @@
|
||||
package org.dromara.jpom.dialect;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.db.dialect.Dialect;
|
||||
import cn.hutool.db.dialect.impl.H2Dialect;
|
||||
import cn.hutool.db.dialect.impl.MysqlDialect;
|
||||
import cn.hutool.db.dialect.impl.PostgresqlDialect;
|
||||
import cn.hutool.db.sql.Wrapper;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import org.dromara.jpom.db.DbExtConfig;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* 数据库方言工具类
|
||||
*
|
||||
* @author whz
|
||||
* @since 2024/3/10
|
||||
*/
|
||||
public class DialectUtil {
|
||||
|
||||
private final static ConcurrentHashMap<DbExtConfig.Mode,Dialect> DIALECT_CACHE = new ConcurrentHashMap<>();
|
||||
private static volatile Wrapper currentDbFieldWrapper;
|
||||
private static final ConcurrentHashMap<String,String> WRAP_FIELD_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
public static Dialect getH2Dialect() {
|
||||
return DIALECT_CACHE.computeIfAbsent(DbExtConfig.Mode.H2, key-> {
|
||||
H2Dialect h2Dialect = new H2Dialect();
|
||||
h2Dialect.setWrapper(new Wrapper('`'));
|
||||
return h2Dialect;
|
||||
});
|
||||
}
|
||||
|
||||
public static Dialect getMySqlDialect() {
|
||||
return DIALECT_CACHE.computeIfAbsent(DbExtConfig.Mode.MYSQL, key->new MysqlDialect());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义postgresql数据库的方言
|
||||
* @return 自定义postgresql数据库的方言
|
||||
*/
|
||||
public static Dialect getPostgresqlDialect() {
|
||||
return DIALECT_CACHE.computeIfAbsent(DbExtConfig.Mode.POSTGRESQL, key->{
|
||||
// 需要特殊处理的列名或表名,需要时在这里添加即可
|
||||
Set<String> names = Stream.of("group","desc","user","content").map(String::toLowerCase).collect(Collectors.toSet());
|
||||
Wrapper wrapper = new Wrapper(){
|
||||
@Override
|
||||
public String wrap(String field) {
|
||||
field = field.toLowerCase();
|
||||
if( field.charAt(0) == '`' && field.charAt(field.length()-1) == '`' ) {
|
||||
field = field.substring(1,field.length()-1);
|
||||
}
|
||||
if( names.contains(field) ) {
|
||||
return super.wrap(field);
|
||||
}
|
||||
return field; // 不属于columns的直接返回
|
||||
}
|
||||
};
|
||||
|
||||
PostgresqlDialect dialect = new PostgresqlDialect();
|
||||
Wrapper innerWrapper = (Wrapper) BeanUtil.getFieldValue(dialect, "wrapper");
|
||||
|
||||
wrapper.setPreWrapQuote(innerWrapper.getPreWrapQuote());
|
||||
wrapper.setSufWrapQuote(innerWrapper.getSufWrapQuote());
|
||||
BeanUtil.setFieldValue(dialect,"wrapper",wrapper);
|
||||
return dialect;
|
||||
});
|
||||
}
|
||||
|
||||
public static Dialect getDialectByMode(DbExtConfig.Mode mode) {
|
||||
switch ( mode ) {
|
||||
case H2: return getH2Dialect();
|
||||
case MYSQL: return getMySqlDialect();
|
||||
case POSTGRESQL: return getPostgresqlDialect();
|
||||
}
|
||||
throw new IllegalArgumentException("未知的数据库方言类型");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表名或列名在当前配置的数据库上的方言,例如 postgresql会使用 " ,mysql使用 `
|
||||
* @param field 表名或者列名
|
||||
* @return 处理后的表名或者列名
|
||||
*/
|
||||
public static String wrapField(String field) {
|
||||
if( currentDbFieldWrapper == null ) {
|
||||
synchronized (DialectUtil.class) {
|
||||
if( currentDbFieldWrapper == null ) {
|
||||
DbExtConfig dbExtConfig = SpringUtil.getBean(DbExtConfig.class);
|
||||
if( dbExtConfig == null || dbExtConfig.getMode() == null ) {
|
||||
throw new IllegalStateException("数据库Mode配置缺失");
|
||||
}
|
||||
Dialect dialectByMode = getDialectByMode(dbExtConfig.getMode());
|
||||
currentDbFieldWrapper = dialectByMode.getWrapper();
|
||||
}
|
||||
}
|
||||
}
|
||||
return WRAP_FIELD_MAP.computeIfAbsent(field,key-> currentDbFieldWrapper.wrap(field) );
|
||||
}
|
||||
}
|
@ -147,6 +147,7 @@ public class H2TableBuilderImpl implements IStorageSqlBuilderService {
|
||||
case "INTEGER":
|
||||
stringBuilder.append("INTEGER").append(StrUtil.SPACE);
|
||||
break;
|
||||
case "BOOLEAN":
|
||||
case "TINYINT":
|
||||
stringBuilder.append("TINYINT").append(StrUtil.SPACE);
|
||||
break;
|
||||
|
@ -160,6 +160,7 @@ public class MysqlTableBuilderImpl implements IStorageSqlBuilderService {
|
||||
case "INTEGER":
|
||||
stringBuilder.append("int").append(StrUtil.SPACE);
|
||||
break;
|
||||
case "BOOLEAN":
|
||||
case "TINYINT":
|
||||
stringBuilder.append("TINYINT").append(StrUtil.SPACE);
|
||||
break;
|
||||
|
39
modules/storage-module/storage-module-postgresql/pom.xml
Normal file
39
modules/storage-module/storage-module-postgresql/pom.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>jpom-storage-module-parent</artifactId>
|
||||
<groupId>org.dromara.jpom.storage-module</groupId>
|
||||
<version>2.11.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>storage-module-postgresql</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>8</maven.compiler.source>
|
||||
<maven.compiler.target>8</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.jpom.storage-module</groupId>
|
||||
<artifactId>storage-module-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.jpom</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<scope>provided</scope>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Code Technology Studio
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package org.dromara.jpom.storage;
|
||||
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import cn.hutool.db.ds.DSFactory;
|
||||
import cn.hutool.setting.Setting;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.db.DbExtConfig;
|
||||
import org.dromara.jpom.db.IStorageService;
|
||||
import org.dromara.jpom.system.JpomRuntimeException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* @author whz
|
||||
* @since 2024/03/06
|
||||
*/
|
||||
@Slf4j
|
||||
public class PostgresqlStorageServiceImpl implements IStorageService {
|
||||
|
||||
private String dbUrl;
|
||||
private DSFactory dsFactory;
|
||||
|
||||
@Override
|
||||
public String dbUrl() {
|
||||
Assert.hasText(this.dbUrl, "还没有初始化数据库");
|
||||
return dbUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchSize() {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DbExtConfig.Mode mode() {
|
||||
return DbExtConfig.Mode.POSTGRESQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DSFactory init(DbExtConfig dbExtConfig) {
|
||||
Assert.isNull(this.dsFactory, "不要重复初始化数据库");
|
||||
Assert.hasText(dbExtConfig.getUrl(), "没有配置数据库连接");
|
||||
Setting setting = dbExtConfig.toSetting();
|
||||
this.dsFactory = DSFactory.create(setting);
|
||||
this.dbUrl = dbExtConfig.getUrl();
|
||||
return this.dsFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DSFactory create(DbExtConfig dbExtConfig, String url, String user, String pass) {
|
||||
Setting setting = this.createSetting(dbExtConfig, url, user, pass);
|
||||
return DSFactory.create(setting);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Setting createSetting(DbExtConfig dbExtConfig, String url, String user, String pass) {
|
||||
String url2 = Opt.ofBlankAble(url).orElse(dbExtConfig.getUrl());
|
||||
String user2 = Opt.ofBlankAble(user).orElse(dbExtConfig.getUserName());
|
||||
String pass2 = Opt.ofBlankAble(pass).orElse(dbExtConfig.getUserPwd());
|
||||
Setting setting = dbExtConfig.toSetting();
|
||||
setting.set("user", user2);
|
||||
setting.set("pass", pass2);
|
||||
setting.set("url", url2);
|
||||
return setting;
|
||||
}
|
||||
|
||||
public DSFactory getDsFactory() {
|
||||
Assert.notNull(this.dsFactory, "还没有初始化数据库");
|
||||
return dsFactory;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JpomRuntimeException warpException(Exception e) {
|
||||
return new JpomRuntimeException("数据库异常", e);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
log.info("postgresql db destroy");
|
||||
if (this.dsFactory != null) {
|
||||
dsFactory.destroy();
|
||||
this.dsFactory = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,308 @@
|
||||
/*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Code Technology Studio
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package org.dromara.jpom.storage;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.BooleanUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.db.sql.Wrapper;
|
||||
import org.dromara.jpom.db.*;
|
||||
import org.dromara.jpom.dialect.DialectUtil;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* postgresql的sql语句构建类
|
||||
* 目前不管列名还是表名,索引名,都会转化为小写的形式,而特殊的关键字会通过Dialect的Wrapper处理
|
||||
* @author whz
|
||||
* @since 2024/3/10
|
||||
*/
|
||||
public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
|
||||
|
||||
@Override
|
||||
public DbExtConfig.Mode mode() {
|
||||
return DbExtConfig.Mode.POSTGRESQL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateIndexSql(List<TableViewIndexData> row) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Wrapper fieldWrapper = DialectUtil.getPostgresqlDialect().getWrapper();
|
||||
for (TableViewIndexData viewIndexData : row) {
|
||||
String indexType = viewIndexData.getIndexType();
|
||||
// 存储过程对大小写敏感,因此传入的 表名,索引名,列名都转需要为小写
|
||||
String name = viewIndexData.getName().toLowerCase();
|
||||
String field = viewIndexData.getField();
|
||||
String tableName = viewIndexData.getTableName().toLowerCase();
|
||||
|
||||
List<String> fields = StrUtil.splitTrim(field, "+").stream()
|
||||
.map(fieldWrapper::wrap).collect(Collectors.toList());
|
||||
/**
|
||||
* CALL drop_index_if_exists('表名','索引名');
|
||||
* CREATE UNIQUE INDEX 索引名 ON 表名 (field1,field2,...);
|
||||
* CREATE INDEX 索引名 ON 表名 (field1,field2,...);
|
||||
* field需要通过wrapper处理
|
||||
*/
|
||||
switch (indexType) {
|
||||
case "ADD-UNIQUE":{
|
||||
Assert.notEmpty(fields, "索引未配置字段");
|
||||
stringBuilder.append("CALL drop_index_if_exists('").append(tableName).append("','").append(name).append("')").append(";").append(StrUtil.LF);
|
||||
stringBuilder.append(this.delimiter()).append(StrUtil.LF);
|
||||
stringBuilder.append("CREATE UNIQUE INDEX ").append(name)
|
||||
.append(" ON ").append(tableName).append(" (").append(CollUtil.join(fields, StrUtil.COMMA)).append(")")
|
||||
.append(";");
|
||||
break;
|
||||
}
|
||||
case "ADD": {
|
||||
Assert.notEmpty(fields, "索引未配置字段");
|
||||
stringBuilder.append("CALL drop_index_if_exists('").append(tableName).append("','").append(name).append("')").append(";").append(StrUtil.LF);
|
||||
stringBuilder.append(this.delimiter()).append(StrUtil.LF);
|
||||
stringBuilder.append("CREATE INDEX ").append(name)
|
||||
.append(" ON ").append(tableName).append(" (").append(CollUtil.join(fields, StrUtil.COMMA)).append(")")
|
||||
.append(";");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的类型:" + indexType);
|
||||
}
|
||||
stringBuilder.append(";").append(StrUtil.LF);
|
||||
stringBuilder.append(this.delimiter()).append(StrUtil.LF);
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateAlterTableSql(List<TableViewAlterData> row) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Wrapper fieldWrapper = DialectUtil.getPostgresqlDialect().getWrapper();
|
||||
for (TableViewAlterData viewAlterData : row) {
|
||||
String alterType = viewAlterData.getAlterType();
|
||||
String tableName = fieldWrapper.wrap(viewAlterData.getTableName());
|
||||
String columnName = viewAlterData.getName().toLowerCase(); //不使用wrapper,存储过程调用时,column不需要包裹
|
||||
switch (alterType) {
|
||||
case "DROP":
|
||||
stringBuilder.append("CALL drop_column_if_exists('").append(tableName).append("', '").append(columnName).append("')");
|
||||
break;
|
||||
case "ADD":
|
||||
stringBuilder.append("CALL add_column_if_not_exists('").append(tableName).append("','")
|
||||
.append(columnName).append("','");
|
||||
stringBuilder.append(generateAddColumnSql(viewAlterData,true));
|
||||
stringBuilder.append("');");
|
||||
/**
|
||||
* 添加列时,因为不能同时指定注释内容,单独加一个语句设置注释
|
||||
* COMMENT ON COLUMN 表名.field IS '注释内容'
|
||||
* field需要通过wrapper处理
|
||||
*/
|
||||
if( StrUtil.isNotBlank(viewAlterData.getComment()) ){
|
||||
stringBuilder.append(delimiter()).append(StrUtil.LF);
|
||||
stringBuilder.append("COMMENT ON COLUMN ").append(tableName)
|
||||
.append(StrUtil.DOT).append(fieldWrapper.wrap(viewAlterData.getName())).append(" IS ")
|
||||
.append("'").append(viewAlterData.getComment().trim()).append("';");
|
||||
}
|
||||
break;
|
||||
case "ALTER":
|
||||
stringBuilder.append(generateAlterSql(viewAlterData));
|
||||
break;
|
||||
case "DROP-TABLE":
|
||||
stringBuilder.append("drop table if exists ").append(viewAlterData.getTableName());
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的类型:" + alterType);
|
||||
}
|
||||
stringBuilder.append(";").append(StrUtil.LF);
|
||||
stringBuilder.append(this.delimiter()).append(StrUtil.LF);
|
||||
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name 表名
|
||||
* @param desc 描述
|
||||
* @param row 字段信息
|
||||
* @return sql
|
||||
*/
|
||||
@Override
|
||||
public String generateTableSql(String name, String desc, List<TableViewData> row) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
Wrapper fieldWrapper = DialectUtil.getPostgresqlDialect().getWrapper();
|
||||
String tableName = fieldWrapper.wrap(name);
|
||||
|
||||
stringBuilder.append("CREATE TABLE IF NOT EXISTS ").append(tableName).append(StrUtil.LF);
|
||||
stringBuilder.append("(").append(StrUtil.LF);
|
||||
for (TableViewData tableViewData : row) {
|
||||
stringBuilder.append(StrUtil.TAB).append(this.generateColumnSql(tableViewData)).append(StrUtil.COMMA).append(StrUtil.LF);
|
||||
}
|
||||
// 主键
|
||||
List<String> primaryKeys = row.stream()
|
||||
.filter(tableViewData -> tableViewData.getPrimaryKey() != null && tableViewData.getPrimaryKey())
|
||||
.map(viewData->fieldWrapper.wrap(viewData.getName()))
|
||||
.collect(Collectors.toList());
|
||||
Assert.notEmpty(primaryKeys, "表没有主键");
|
||||
stringBuilder.append(StrUtil.TAB).append("PRIMARY KEY (").append(CollUtil.join(primaryKeys, StrUtil.COMMA)).append(")").append(StrUtil.LF);
|
||||
stringBuilder.append(");").append(StrUtil.LF);
|
||||
// 表注释
|
||||
stringBuilder.append("COMMENT ON TABLE ").append(fieldWrapper.wrap(name)).append(" IS '").append(desc).append("';");
|
||||
|
||||
|
||||
// 建表语句的列注释需要通过单独的sql语句设置
|
||||
for (TableViewData tableViewData : row) {
|
||||
if( StrUtil.isNotBlank(tableViewData.getComment()) ) {
|
||||
stringBuilder.append(delimiter()).append(StrUtil.LF);
|
||||
stringBuilder.append("CALL exec_if_column_exists('")
|
||||
.append(tableName).append("','")
|
||||
.append(tableViewData.getName()).append("','")
|
||||
.append("COMMENT ON COLUMN ").append(tableName)
|
||||
.append(StrUtil.DOT).append(fieldWrapper.wrap(tableViewData.getName())).append(" IS ")
|
||||
.append("''").append(tableViewData.getComment().trim()).append("'' ');");
|
||||
}
|
||||
}
|
||||
return stringBuilder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateColumnSql(TableViewRowData tableViewRowData) {
|
||||
return generateAddColumnSql(tableViewRowData,false);
|
||||
}
|
||||
|
||||
|
||||
private String generateAddColumnSql(TableViewRowData tableViewRowData,boolean encode) {
|
||||
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
Wrapper fieldWrapper = DialectUtil.getPostgresqlDialect().getWrapper();
|
||||
String type = getColumnTypeStr(tableViewRowData.getType(),tableViewRowData.getLen());
|
||||
strBuilder.append(StrUtil.SPACE).append(fieldWrapper.wrap(tableViewRowData.getName()))
|
||||
.append(StrUtil.SPACE).append(type);
|
||||
|
||||
String defaultValue = tableViewRowData.getDefaultValue();
|
||||
if (StrUtil.isNotEmpty(defaultValue)) {
|
||||
if( "BOOLEAN".equals(type) ) {
|
||||
defaultValue = Boolean.toString(BooleanUtil.toBoolean(defaultValue.trim()));
|
||||
}
|
||||
strBuilder.append(" DEFAULT '").append(defaultValue).append("'");
|
||||
}
|
||||
|
||||
Boolean notNull = tableViewRowData.getNotNull();
|
||||
if (notNull != null && notNull) {
|
||||
strBuilder.append(" NOT NULL ");
|
||||
}
|
||||
String columnSql = strBuilder.toString();
|
||||
columnSql = encode ? StrUtil.replace(columnSql, "'", "''") : columnSql;
|
||||
int length = StrUtil.length(columnSql);
|
||||
Assert.state(length <= 180, "sql 语句太长啦");
|
||||
return columnSql;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成postgresql的alter语句
|
||||
* postgresql不像 h2或mysql可以一个alter同时设置 数据类型,默认值,非空,注释,因此需生成多条sql语句才能实现功能
|
||||
* @param viewAlterData
|
||||
* @return
|
||||
*/
|
||||
private String generateAlterSql(TableViewAlterData viewAlterData) {
|
||||
|
||||
Wrapper fieldWrapper = DialectUtil.getPostgresqlDialect().getWrapper();
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
String tableName = fieldWrapper.wrap(viewAlterData.getTableName());
|
||||
String name = fieldWrapper.wrap(viewAlterData.getName());
|
||||
|
||||
// 先改类型
|
||||
String type = getColumnTypeStr(viewAlterData.getType(),viewAlterData.getLen());
|
||||
strBuilder.append("ALTER TABLE ").append(tableName)
|
||||
.append(" ALTER COLUMN ").append(name)
|
||||
.append(" TYPE ").append(type).append(";");
|
||||
|
||||
// 再设置默认值
|
||||
strBuilder.append(delimiter()).append(StrUtil.LF);
|
||||
String defaultValue = viewAlterData.getDefaultValue();
|
||||
strBuilder.append("ALTER TABLE ").append(tableName)
|
||||
.append(" ALTER COLUMN ").append(name)
|
||||
.append(" SET DEFAULT '");
|
||||
if (StrUtil.isNotEmpty(defaultValue)) {
|
||||
if( "BOOLEAN".equals(type) ) {
|
||||
defaultValue = Boolean.toString(BooleanUtil.toBoolean(defaultValue.trim()));
|
||||
}
|
||||
strBuilder.append(defaultValue).append("';");
|
||||
}else {
|
||||
strBuilder.append("NULL").append("';");
|
||||
}
|
||||
|
||||
// 设置非空
|
||||
strBuilder.append(delimiter()).append(StrUtil.LF);
|
||||
strBuilder.append("ALTER TABLE ").append(tableName)
|
||||
.append(" ALTER COLUMN ").append(name);
|
||||
Boolean notNull = viewAlterData.getNotNull();
|
||||
if (notNull != null && notNull) {
|
||||
strBuilder.append(" SET NOT NULL ").append(";");
|
||||
}else {
|
||||
strBuilder.append(" DROP NOT NULL ").append(";");
|
||||
}
|
||||
|
||||
// 注释
|
||||
strBuilder.append(delimiter()).append(StrUtil.LF);
|
||||
String comment = viewAlterData.getComment();
|
||||
comment = StrUtil.isEmpty(comment) ? StrUtil.EMPTY : comment.trim();
|
||||
strBuilder.append("COMMENT ON COLUMN ").append(tableName)
|
||||
.append(StrUtil.DOT).append(name).append(" IS ")
|
||||
.append("'").append(comment).append("';");
|
||||
|
||||
Assert.state(strBuilder.length() <= 1000, "sql 语句太长啦");
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String delimiter() {
|
||||
return "-- postgresql $delimiter$";
|
||||
}
|
||||
|
||||
|
||||
private String getColumnTypeStr(String type,Integer dataLen) {
|
||||
Assert.hasText(type, "未正确配置数据类型");
|
||||
type = type.toUpperCase();
|
||||
switch (type) {
|
||||
case "LONG":
|
||||
return "BIGINT";
|
||||
case "STRING":
|
||||
return "VARCHAR(" + ObjectUtil.defaultIfNull(dataLen, 255) + ")";
|
||||
case "TEXT":
|
||||
return "TEXT";
|
||||
case "INTEGER":
|
||||
return "INTEGER";
|
||||
case "BOOLEAN":
|
||||
return "BOOLEAN";
|
||||
case "TINYINT":
|
||||
return "SMALLINT";
|
||||
case "FLOAT":
|
||||
return "REAL";
|
||||
case "DOUBLE":
|
||||
return "DOUBLE PRECISION";
|
||||
default:
|
||||
throw new IllegalArgumentException("不支持的数据类型:" + type);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
org.dromara.jpom.storage.PostgresqlStorageServiceImpl
|
@ -0,0 +1 @@
|
||||
org.dromara.jpom.storage.PostgresqlTableBuilderImpl
|
Loading…
Reference in New Issue
Block a user