🐞 fix(服务端): 无法查询到分组

This commit is contained in:
bwcx_jzy 2024-04-11 09:06:28 +08:00
parent 6d982ab09c
commit 579fcfcc08
No known key found for this signature in database
GPG Key ID: E187D6E9DDDE8C53
3 changed files with 66 additions and 90 deletions

View File

@ -37,7 +37,6 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 数据存储服务
@ -70,9 +69,9 @@ public class StorageServiceFactory {
/**
* 将数据迁移到当前环境
*/
public static void migrateH2ToNow(DbExtConfig dbExtConfig, String h2Url, String h2User, String h2Pass,DbExtConfig.Mode targetNode) {
public static void migrateH2ToNow(DbExtConfig dbExtConfig, String h2Url, String h2User, String h2Pass, DbExtConfig.Mode targetNode) {
log.info("开始迁移 h2 数据到 {}", dbExtConfig.getMode());
Assert.notNull(mode,"未指定目标数据库信息");
Assert.notNull(mode, "未指定目标数据库信息");
try {
IStorageService h2StorageService = doCreateStorageService(DbExtConfig.Mode.H2);
boolean hasDbData = h2StorageService.hasDbData();
@ -101,20 +100,20 @@ public class StorageServiceFactory {
log.info("成功连接 {} {}", dbExtConfig.getMode(), dbExtConfig.getUrl());
Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation("org.dromara.jpom", TableName.class);
classes = classes.stream()
.filter(aClass -> {
TableName tableName = aClass.getAnnotation(TableName.class);
DbExtConfig.Mode[] modes = tableName.modes();
if (ArrayUtil.isEmpty(modes)) {
return true;
}
return ArrayUtil.contains(modes, dbExtConfig.getMode());
})
.sorted((o1, o2) -> StrUtil.compare(o1.getSimpleName(), o2.getSimpleName(), false))
.collect(Collectors.toCollection(LinkedHashSet::new));
.filter(aClass -> {
TableName tableName = aClass.getAnnotation(TableName.class);
DbExtConfig.Mode[] modes = tableName.modes();
if (ArrayUtil.isEmpty(modes)) {
return true;
}
return ArrayUtil.contains(modes, dbExtConfig.getMode());
})
.sorted((o1, o2) -> StrUtil.compare(o1.getSimpleName(), o2.getSimpleName(), false))
.collect(Collectors.toCollection(LinkedHashSet::new));
log.info("准备迁移数据");
int total = 0;
for (Class<?> aClass : classes) {
total += migrateH2ToNowItem(aClass, h2DsFactory, nowDsFactory,targetNode);
total += migrateH2ToNowItem(aClass, h2DsFactory, nowDsFactory, targetNode);
}
long endTime = SystemClock.now();
log.info("迁移完成,累计迁移 {} 条数据,耗时:{}", total, DateUtil.formatBetween(endTime - time));
@ -128,7 +127,7 @@ public class StorageServiceFactory {
}
}
private static int migrateH2ToNowItem(Class<?> aClass, DSFactory h2DsFactory, DSFactory targetDsFactory,DbExtConfig.Mode targetNode) throws SQLException {
private static int migrateH2ToNowItem(Class<?> aClass, DSFactory h2DsFactory, DSFactory targetDsFactory, DbExtConfig.Mode targetNode) throws SQLException {
TableName tableName = aClass.getAnnotation(TableName.class);
Wrapper targetModeWrapper = DialectUtil.getDialectByMode(targetNode).getWrapper();
Set<String> boolFieldSet = Arrays.stream(ReflectUtil.getFields(aClass, field -> Boolean.class.equals(field.getType()) || boolean.class.equals(field.getType())))
@ -140,7 +139,7 @@ public class StorageServiceFactory {
while (true) {
Entity where = Entity.create(tableName.value());
PageResult<Entity> pageResult;
Db db = Db.use(h2DsFactory.getDataSource(),DialectUtil.getH2Dialect());
Db db = Db.use(h2DsFactory.getDataSource(), DialectUtil.getH2Dialect());
Page page = new Page(1, 200);
pageResult = db.page(where, page);
if (pageResult.isEmpty()) {
@ -154,12 +153,12 @@ public class StorageServiceFactory {
Entity entity = Entity.create(targetModeWrapper.wrap(tableName.value()));
return entity.parseBean(o, false, true);
}).peek(entity -> {
if( DbExtConfig.Mode.POSTGRESQL.equals(targetNode) ) {
if (DbExtConfig.Mode.POSTGRESQL.equals(targetNode)) {
// tinyint类型查出来是数字需转为bool
boolFieldSet.forEach(fieldName->{
boolFieldSet.forEach(fieldName -> {
Object field = entity.get(fieldName);
if( field instanceof Number ) {
entity.set(fieldName,BooleanUtil.toBoolean(field.toString()));
if (field instanceof Number) {
entity.set(fieldName, BooleanUtil.toBoolean(field.toString()));
}
});
}
@ -174,18 +173,18 @@ public class StorageServiceFactory {
}
total += newResult.size();
// 插入信息数据
Db db2 = Db.use(targetDsFactory.getDataSource(),DialectUtil.getDialectByMode(targetNode));
Db db2 = Db.use(targetDsFactory.getDataSource(), DialectUtil.getDialectByMode(targetNode));
Connection connection = db2.getConnection();
try {
SqlConnRunner runner = db2.getRunner();
for (Entity entity : newResult) {
// hutool的批量insert方法有坑可能导致部分参数被丢弃
runner.insert(connection,entity);
runner.insert(connection, entity);
}
}catch (Exception e){
} catch (Exception e) {
throw new RuntimeException(e);
}finally {
} finally {
db2.closeConnection(connection);
}

View File

@ -9,8 +9,6 @@
*/
package org.dromara.jpom.dialect;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.dialect.Dialect;
import cn.hutool.db.dialect.impl.H2Dialect;
import cn.hutool.db.dialect.impl.MysqlDialect;
@ -19,10 +17,7 @@ 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;
/**
* 数据库方言工具类
@ -34,12 +29,15 @@ 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<>();
/**
* 通用的包裹器
*/
private static final Wrapper COMMON_WRAPPER = new Wrapper('`');
public static Dialect getH2Dialect() {
return DIALECT_CACHE.computeIfAbsent(DbExtConfig.Mode.H2, key -> {
H2Dialect h2Dialect = new H2Dialect();
h2Dialect.setWrapper(new Wrapper('`'));
h2Dialect.setWrapper(COMMON_WRAPPER);
return h2Dialect;
});
}
@ -55,28 +53,22 @@ public class DialectUtil {
*/
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() {
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; // 不属于names的直接返回
// 代码中存在固定编码 warp
String unWrap = COMMON_WRAPPER.unWrap(field);
return super.wrap(unWrap);
}
@Override
public String unWrap(String field) {
String unWrap = COMMON_WRAPPER.unWrap(field);
return super.unWrap(unWrap);
}
};
PostgresqlDialect dialect = new PostgresqlDialect();
Wrapper innerWrapper = (Wrapper) BeanUtil.getFieldValue(dialect, "wrapper");
wrapper.setPreWrapQuote(innerWrapper.getPreWrapQuote());
wrapper.setSufWrapQuote(innerWrapper.getSufWrapQuote());
BeanUtil.setFieldValue(dialect, "wrapper", wrapper);
dialect.setWrapper(wrapper);
return dialect;
});
}
@ -90,8 +82,9 @@ public class DialectUtil {
return getMySqlDialect();
case POSTGRESQL:
return getPostgresqlDialect();
default:
throw new IllegalArgumentException("未知的数据库方言类型:" + mode);
}
throw new IllegalArgumentException("未知的数据库方言类型");
}
/**
@ -102,7 +95,7 @@ public class DialectUtil {
*/
public static String wrapField(String field) {
initWrapFieldMap();
return WRAP_FIELD_MAP.computeIfAbsent(field, key -> currentDbFieldWrapper.wrap(field));
return currentDbFieldWrapper.wrap(field);
}
/**
@ -125,24 +118,6 @@ public class DialectUtil {
public static String unWrapField(String field) {
initWrapFieldMap();
return WRAP_FIELD_MAP.computeIfAbsent(field, s -> {
Character preWrapQuote = currentDbFieldWrapper.getPreWrapQuote();
Character sufWrapQuote = currentDbFieldWrapper.getSufWrapQuote();
if (preWrapQuote == null || sufWrapQuote == null || StrUtil.isBlank(s)) {
return s;
}
//如果已经包含包装的引号返回原字符
if (!StrUtil.isSurround(s, preWrapQuote, sufWrapQuote)) {
return s;
}
//如果字段中包含通配符或者括号字段通配符或者函数不做包装
if (StrUtil.containsAnyIgnoreCase(s, "*", "(", " ", " as ")) {
return s;
}
return StrUtil.removeSuffix(StrUtil.removePrefix(s, preWrapQuote.toString()), sufWrapQuote.toString());
});
return currentDbFieldWrapper.unWrap(field);
}
}

View File

@ -23,6 +23,7 @@ import java.util.stream.Collectors;
/**
* postgresql的sql语句构建类
* 目前不管列名还是表名索引名都会转化为小写的形式而特殊的关键字会通过Dialect的Wrapper处理
*
* @author whz
* @since 2024/3/10
*/
@ -36,7 +37,7 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
public PostgresqlTableBuilderImpl() {
Set<Class<?>> modelClassSet = ClassUtil.scanPackageByAnnotation("org.dromara.jpom", TableName.class);
this.tableName2ModelBoolFieldNameSetMap = new HashMap<>();
modelClassSet.forEach(modelClass->{
modelClassSet.forEach(modelClass -> {
TableName annotation = modelClass.getAnnotation(TableName.class);
// 统一处理成小写model也应该不会出现转为小写后重名的field
String tableName = annotation.value().toLowerCase();
@ -44,7 +45,7 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
Set<String> nameSet = Arrays.stream(boolFieldArr)
.map(field -> field.getName().toLowerCase())
.collect(Collectors.toSet());
tableName2ModelBoolFieldNameSetMap.put(tableName,nameSet);
tableName2ModelBoolFieldNameSetMap.put(tableName, nameSet);
});
}
@ -73,7 +74,7 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
* field需要通过wrapper处理
*/
switch (indexType) {
case "ADD-UNIQUE":{
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);
@ -115,14 +116,14 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
case "ADD":
stringBuilder.append("CALL add_column_if_not_exists('").append(tableName).append("','")
.append(columnName).append("','");
stringBuilder.append(generateColumnSql(viewAlterData.getTableName(),viewAlterData,true));
stringBuilder.append(generateColumnSql(viewAlterData.getTableName(), viewAlterData, true));
stringBuilder.append("');");
/**
* 添加列时因为不能同时指定注释内容单独加一个语句设置注释
* COMMENT ON COLUMN 表名.field IS '注释内容'
* field需要通过wrapper处理
*/
if( StrUtil.isNotBlank(viewAlterData.getComment()) ){
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 ")
@ -160,13 +161,13 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
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(tableName,tableViewData)).append(StrUtil.COMMA).append(StrUtil.LF);
stringBuilder.append(StrUtil.TAB).append(this.generateColumnSql(tableName, 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());
.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);
@ -176,7 +177,7 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
// 建表语句的列注释需要通过单独的sql语句设置
for (TableViewData tableViewData : row) {
if( StrUtil.isNotBlank(tableViewData.getComment()) ) {
if (StrUtil.isNotBlank(tableViewData.getComment())) {
stringBuilder.append(delimiter()).append(StrUtil.LF);
stringBuilder.append("CALL exec_if_column_exists('")
.append(tableName).append("','")
@ -196,11 +197,12 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
@Override
public String generateColumnSql(String tableName, TableViewRowData tableViewRowData) {
return generateColumnSql(tableName,tableViewRowData,false);
return generateColumnSql(tableName, tableViewRowData, false);
}
/**
* 生成 alter add create table 时的列定义
*
* @param tableName
* @param tableViewRowData
* @param encode
@ -210,14 +212,14 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
StringBuilder strBuilder = new StringBuilder();
Wrapper fieldWrapper = DialectUtil.getPostgresqlDialect().getWrapper();
String type = getColumnTypeStr(tableName,tableViewRowData.getName(),
tableViewRowData.getType(),tableViewRowData.getLen());
String type = getColumnTypeStr(tableName, tableViewRowData.getName(),
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) ) {
if ("BOOLEAN".equals(type)) {
defaultValue = Boolean.toString(BooleanUtil.toBoolean(defaultValue.trim()));
}
strBuilder.append(" DEFAULT '").append(defaultValue).append("'");
@ -237,6 +239,7 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
/**
* 生成postgresql的alter语句
* postgresql不像 h2或mysql可以一个alter同时设置 数据类型默认值非空注释因此需生成多条sql语句才能实现功能
*
* @param viewAlterData
* @return
*/
@ -248,8 +251,8 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
String name = fieldWrapper.wrap(viewAlterData.getName());
// 先改类型
String type = getColumnTypeStr(viewAlterData.getTableName(),viewAlterData.getName()
,viewAlterData.getType(),viewAlterData.getLen());
String type = getColumnTypeStr(viewAlterData.getTableName(), viewAlterData.getName()
, viewAlterData.getType(), viewAlterData.getLen());
strBuilder.append("ALTER TABLE ").append(tableName)
.append(" ALTER COLUMN ").append(name)
.append(" TYPE ").append(type).append(";");
@ -261,11 +264,11 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
.append(" ALTER COLUMN ").append(name)
.append(" SET DEFAULT '");
if (StrUtil.isNotEmpty(defaultValue)) {
if( "BOOLEAN".equals(type) ) {
if ("BOOLEAN".equals(type)) {
defaultValue = Boolean.toString(BooleanUtil.toBoolean(defaultValue.trim()));
}
strBuilder.append(defaultValue).append("';");
}else {
} else {
strBuilder.append("NULL").append("';");
}
@ -276,7 +279,7 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
Boolean notNull = viewAlterData.getNotNull();
if (notNull != null && notNull) {
strBuilder.append(" SET NOT NULL ").append(";");
}else {
} else {
strBuilder.append(" DROP NOT NULL ").append(";");
}
@ -293,14 +296,13 @@ public class PostgresqlTableBuilderImpl implements IStorageSqlBuilderService {
}
@Override
public String delimiter() {
return "-- postgresql $delimiter$";
}
private String getColumnTypeStr(String tableName,String columnName,String type,Integer dataLen) {
private String getColumnTypeStr(String tableName, String columnName, String type, Integer dataLen) {
Assert.hasText(type, "未正确配置数据类型");
type = type.toUpperCase();
switch (type) {