remove yarn.lock

This commit is contained in:
QAIU
2025-05-08 18:18:15 +08:00
parent aff8f88076
commit b77930adfb
22 changed files with 829 additions and 219 deletions

View File

@@ -64,6 +64,12 @@
<artifactId>mysql-connector-j</artifactId>
<version>9.2.0</version>
</dependency>
<!-- PG驱动-->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.7.3</version>
</dependency>
</dependencies>

View File

@@ -21,7 +21,6 @@ public @interface Constraint {
boolean notNull() default false;
/**
* 唯一键约束 TODO 待实现
* @return 唯一键约束
*/
String uniqueKey() default "";

View File

@@ -8,7 +8,7 @@ import io.vertx.codegen.format.LowerCamelCase;
import io.vertx.codegen.format.SnakeCase;
import io.vertx.core.Future;
import io.vertx.core.Promise;
import io.vertx.jdbcclient.JDBCPool;
import io.vertx.sqlclient.Pool;
import io.vertx.sqlclient.templates.annotations.Column;
import io.vertx.sqlclient.templates.annotations.RowMapped;
import org.apache.commons.lang3.StringUtils;
@@ -24,164 +24,312 @@ import java.util.*;
* @author <a href="https://qaiu.top">QAIU</a>
*/
public class CreateTable {
public static Map<Class<?>, String> javaProperty2SqlColumnMap = new HashMap<>();
public static Map<Class<?>, String> javaProperty2SqlColumnMap = new HashMap<>() {{
// Java类型到SQL类型的映射
put(Integer.class, "INT");
put(Short.class, "SMALLINT");
put(Byte.class, "TINYINT");
put(Long.class, "BIGINT");
put(java.math.BigDecimal.class, "DECIMAL");
put(Double.class, "DOUBLE");
put(Float.class, "REAL");
put(Boolean.class, "BOOLEAN");
put(String.class, "VARCHAR");
put(Date.class, "TIMESTAMP");
put(java.time.LocalDateTime.class, "TIMESTAMP");
put(java.sql.Timestamp.class, "TIMESTAMP");
put(java.sql.Date.class, "DATE");
put(java.sql.Time.class, "TIME");
// 基本数据类型
put(int.class, "INT");
put(short.class, "SMALLINT");
put(byte.class, "TINYINT");
put(long.class, "BIGINT");
put(double.class, "DOUBLE");
put(float.class, "REAL");
put(boolean.class, "BOOLEAN");
}};
private static final Logger LOGGER = LoggerFactory.getLogger(CreateTable.class);
static {
javaProperty2SqlColumnMap.put(Integer.class, "INT");
javaProperty2SqlColumnMap.put(Short.class, "SMALLINT");
javaProperty2SqlColumnMap.put(Byte.class, "TINYINT");
javaProperty2SqlColumnMap.put(Long.class, "BIGINT");
javaProperty2SqlColumnMap.put(java.math.BigDecimal.class, "DECIMAL");
javaProperty2SqlColumnMap.put(Double.class, "DOUBLE");
javaProperty2SqlColumnMap.put(Float.class, "REAL");
javaProperty2SqlColumnMap.put(Boolean.class, "BOOLEAN");
javaProperty2SqlColumnMap.put(String.class, "VARCHAR");
javaProperty2SqlColumnMap.put(java.util.Date.class, "TIMESTAMP");
javaProperty2SqlColumnMap.put(java.time.LocalDateTime.class, "TIMESTAMP");
javaProperty2SqlColumnMap.put(java.sql.Timestamp.class, "TIMESTAMP");
javaProperty2SqlColumnMap.put(java.sql.Date.class, "DATE");
javaProperty2SqlColumnMap.put(java.sql.Time.class, "TIME");
javaProperty2SqlColumnMap.put(int.class, "INT");
javaProperty2SqlColumnMap.put(short.class, "SMALLINT");
javaProperty2SqlColumnMap.put(byte.class, "TINYINT");
javaProperty2SqlColumnMap.put(long.class, "BIGINT");
javaProperty2SqlColumnMap.put(double.class, "DOUBLE");
javaProperty2SqlColumnMap.put(float.class, "REAL");
javaProperty2SqlColumnMap.put(boolean.class, "BOOLEAN");
}
public static String UNIQUE_PREFIX = "idx_";
private static Case getCase(Class<?> clz) {
switch (clz.getName()) {
case "io.vertx.codegen.format.CamelCase":
return CamelCase.INSTANCE;
case "io.vertx.codegen.format.SnakeCase":
return SnakeCase.INSTANCE;
case "io.vertx.codegen.format.LowerCamelCase":
return LowerCamelCase.INSTANCE;
default:
throw new UnsupportedOperationException();
return switch (clz.getName()) {
case "io.vertx.codegen.format.CamelCase" -> CamelCase.INSTANCE;
case "io.vertx.codegen.format.SnakeCase" -> SnakeCase.INSTANCE;
case "io.vertx.codegen.format.LowerCamelCase" -> LowerCamelCase.INSTANCE;
default -> throw new UnsupportedOperationException();
};
}
public static List<String> getCreateTableSQL(Class<?> clz, JDBCType type) {
// 获取表名和主键
TableInfo tableInfo = extractTableInfo(clz, type);
// 构建表的SQL语句
List<String> sqlList = new ArrayList<>();
StringBuilder sb = new StringBuilder(50);
sb.append("CREATE TABLE IF NOT EXISTS ")
.append(tableInfo.quotationMarks).append(tableInfo.tableName).append(tableInfo.quotationMarks)
.append(" ( \r\n ");
// 处理字段并生成列定义
List<String> indexSQLs = new ArrayList<>();
processFields(clz, tableInfo, sb, indexSQLs);
// 去掉最后一个逗号并添加表尾部信息
String tableSQL = sb.substring(0, sb.lastIndexOf(",")) + tableInfo.endStr;
sqlList.add(tableSQL);
// 添加索引SQL
sqlList.addAll(indexSQLs);
return sqlList;
}
// 修改extractTableInfo方法处理没有Table注解时默认使用id字段作为主键
private static TableInfo extractTableInfo(Class<?> clz, JDBCType type) {
String quotationMarks;
String endStr;
if (type == JDBCType.MySQL) {
quotationMarks = "`";
endStr = ")ENGINE=InnoDB DEFAULT CHARSET=utf8;";
} else {
quotationMarks = "\"";
endStr = ");";
}
String primaryKey = null;
String tableName = null;
Case caseFormat = SnakeCase.INSTANCE;
// 判断类上是否有RowMapped注解
if (clz.isAnnotationPresent(RowMapped.class)) {
RowMapped annotation = clz.getAnnotation(RowMapped.class);
caseFormat = getCase(annotation.formatter());
}
// 判断类上是否有Table注解
if (clz.isAnnotationPresent(Table.class)) {
Table annotation = clz.getAnnotation(Table.class);
tableName = StringUtils.isNotEmpty(annotation.value())
? annotation.value()
: LowerCamelCase.INSTANCE.to(caseFormat, clz.getSimpleName());
primaryKey = annotation.keyFields();
}
// 如果表名仍为null使用类名转下划线命名作为表名
if (StringUtils.isEmpty(tableName)) {
tableName = LowerCamelCase.INSTANCE.to(SnakeCase.INSTANCE, clz.getSimpleName());
}
// 如果主键为空默认使用id字段作为主键
if (StringUtils.isEmpty(primaryKey)) {
try {
clz.getDeclaredField("id");
primaryKey = "id";
} catch (NoSuchFieldException e) {
// 如果没有id字段不设置主键
primaryKey = null;
}
}
return new TableInfo(tableName, quotationMarks, endStr, primaryKey, caseFormat, type);
}
// 修改processFields方法处理索引
private static void processFields(Class<?> clz, TableInfo tableInfo, StringBuilder sb, List<String> indexSQLs) {
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
// 跳过无效字段
if (isIgnoredField(field)) {
continue;
}
// 获取字段名和SQL类型
String column = LowerCamelCase.INSTANCE.to(tableInfo.caseFormat, field.getName());
String sqlType = javaProperty2SqlColumnMap.get(field.getType());
// 处理字段注解
column = processColumnAnnotation(field, column);
int[] decimalSize = {22, 2};
int varcharSize = 255;
if (field.isAnnotationPresent(Length.class)) {
Length length = field.getAnnotation(Length.class);
decimalSize = length.decimalSize();
varcharSize = length.varcharSize();
}
// 构建列定义
sb.append(tableInfo.quotationMarks).append(column).append(tableInfo.quotationMarks)
.append(" ").append(sqlType);
appendTypeLength(sqlType, sb, decimalSize, varcharSize);
appendConstraints(field, sb, tableInfo);
appendPrimaryKey(tableInfo.primaryKey, column, sb);
// 添加索引
appendIndex(tableInfo, indexSQLs, field);
sb.append(",\n ");
}
}
public static String getCreateTableSQL(Class<?> clz, JDBCType type) {
String quotationMarks = type == JDBCType.H2DB ? "\"" : "`";
String endStr = type == JDBCType.H2DB ? ");" : ")ENGINE=InnoDB DEFAULT CHARSET=utf8;";
// 判断类上是否有次注解
String primaryKey = null; // 主键
String tableName = null; // 表名
Case caseFormat = SnakeCase.INSTANCE;
if (clz.isAnnotationPresent(RowMapped.class)) {
RowMapped annotation = clz.getAnnotation(RowMapped.class);
Class<? extends Case> formatter = annotation.formatter();
caseFormat = getCase(formatter);
}
// 判断是否忽略字段
private static boolean isIgnoredField(Field field) {
return field.getName().equals("serialVersionUID")
|| StringUtils.isEmpty(javaProperty2SqlColumnMap.get(field.getType()))
|| field.isAnnotationPresent(TableGenIgnore.class);
}
if (clz.isAnnotationPresent(Table.class)) {
// 获取类上的注解
Table annotation = clz.getAnnotation(Table.class);
// 输出注解上的类名
String tableNameAnnotation = annotation.value();
if (StringUtils.isNotEmpty(tableNameAnnotation)) {
tableName = tableNameAnnotation;
} else {
tableName = LowerCamelCase.INSTANCE.to(caseFormat, clz.getSimpleName());
// 处理Column注解
private static String processColumnAnnotation(Field field, String column) {
if (field.isAnnotationPresent(Column.class)) {
Column columnAnnotation = field.getAnnotation(Column.class);
if (StringUtils.isNotBlank(columnAnnotation.name())) {
column = columnAnnotation.name();
}
primaryKey = annotation.keyFields();
}
Field[] fields = clz.getDeclaredFields();
String column;
int[] decimalSize = {22, 2};
int varcharSize = 255;
StringBuilder sb = new StringBuilder(50);
sb.append("CREATE TABLE IF NOT EXISTS ").append(quotationMarks).append(tableName).append(quotationMarks).append(" ( \r\n ");
boolean firstId = true;
for (Field f : fields) {
Class<?> paramType = f.getType();
String sqlType = javaProperty2SqlColumnMap.get(paramType);
if (f.getName().equals("serialVersionUID") || StringUtils.isEmpty(sqlType) || f.isAnnotationPresent(TableGenIgnore.class)) {
continue;
return column;
}
// 添加类型长度
private static void appendTypeLength(String sqlType, StringBuilder sb, int[] decimalSize, int varcharSize) {
if ("DECIMAL".equals(sqlType)) {
sb.append("(").append(decimalSize[0]).append(",").append(decimalSize[1]).append(")");
} else if ("VARCHAR".equals(sqlType)) {
sb.append("(").append(varcharSize).append(")");
}
}
// 添加约束
private static void appendConstraints(Field field, StringBuilder sb, TableInfo tableInfo) {
JDBCType type = tableInfo.dbType;
if (field.isAnnotationPresent(Constraint.class)) {
Constraint constraint = field.getAnnotation(Constraint.class);
if (constraint.notNull()) {
sb.append(" NOT NULL");
}
column = LowerCamelCase.INSTANCE.to(caseFormat, f.getName());
if (f.isAnnotationPresent(Column.class)) {
Column columnAnnotation = f.getAnnotation(Column.class);
//输出注解属性
if (StringUtils.isNotBlank(columnAnnotation.name())) {
column = columnAnnotation.name();
}
String apostrophe = constraint.defaultValueIsFunction() ? "" : "'";
if (StringUtils.isNotEmpty(constraint.defaultValue())) {
sb.append(" DEFAULT ").append(apostrophe).append(constraint.defaultValue()).append(apostrophe);
}
if (f.isAnnotationPresent(Length.class)) {
Length fieldAnnotation = f.getAnnotation(Length.class);
decimalSize = fieldAnnotation.decimalSize();
varcharSize = fieldAnnotation.varcharSize();
}
sb.append(quotationMarks).append(column).append(quotationMarks);
sb.append(" ").append(sqlType);
// 添加类型长度
if (sqlType.equals("DECIMAL")) {
sb.append("(").append(decimalSize[0]).append(",").append(decimalSize[1]).append(")");
}
if (sqlType.equals("VARCHAR")) {
sb.append("(").append(varcharSize).append(")");
}
if (f.isAnnotationPresent(Constraint.class)) {
Constraint constraintAnnotation = f.getAnnotation(Constraint.class);
if (constraintAnnotation.notNull()) {
//非空约束
sb.append(" NOT NULL");
}
String apostrophe = constraintAnnotation.defaultValueIsFunction() ? "" : "'";
if (StringUtils.isNotEmpty(constraintAnnotation.defaultValue())) {
//默认值约束
sb.append(" DEFAULT ").append(apostrophe).append(constraintAnnotation.defaultValue()).append(apostrophe);
}
if (constraintAnnotation.autoIncrement() && paramType.equals(Integer.class) || paramType.equals(Long.class)) {
////自增
if (constraint.autoIncrement()) {
if (type == JDBCType.PostgreSQL) {
// 需要移除字段类型(最后一个单词)
if (field.getType().equals(Integer.class)) {
sb.delete(sb.lastIndexOf(" "), sb.length());
sb.append(" SERIAL");
} else if (field.getType().equals(Long.class)) {
sb.delete(sb.lastIndexOf(" "), sb.length());
sb.append(" BIGSERIAL");
}
} else if (field.getType().equals(Integer.class) || field.getType().equals(Long.class)) {
sb.append(" AUTO_INCREMENT");
}
}
if (StringUtils.isEmpty(primaryKey)) {
if (firstId) {//类型转换
sb.append(" PRIMARY KEY");
firstId = false;
}
} else {
if (primaryKey.equals(column.toLowerCase())) {
sb.append(" PRIMARY KEY");
}
}
// 添加主键
private static void appendPrimaryKey(String primaryKey, String column, StringBuilder sb) {
if (StringUtils.isEmpty(primaryKey)) {
return;
}
if (primaryKey.equalsIgnoreCase(column)) {
sb.append(" PRIMARY KEY");
}
}
private static void appendIndex(TableInfo tableInfo, List<String> indexSQLs, Field field) {
if (!field.isAnnotationPresent(Constraint.class)) {
return;
}
Constraint constraint = field.getAnnotation(Constraint.class);
if (StringUtils.isEmpty(constraint.uniqueKey())) {
return;
}
String indexName = UNIQUE_PREFIX + tableInfo.tableName + "_" + constraint.uniqueKey();
String columnName = field.getName();
// 检查是否已有相同索引名称的索引
Optional<String> existingIndex = indexSQLs.stream()
.filter(sql -> sql.contains(tableInfo.quotationMarks + indexName + tableInfo.quotationMarks))
.findFirst();
if (existingIndex.isPresent()) {
// 如果存在相同索引名称,追加字段到索引定义中
String updatedIndex = existingIndex.get().replaceFirst(
"\\(([^)]+)\\)", // 匹配索引字段列表
"($1, " + tableInfo.quotationMarks + columnName + tableInfo.quotationMarks + ")"
);
indexSQLs.remove(existingIndex.get());
indexSQLs.add(updatedIndex);
} else {
// 如果不存在相同索引名称,创建新的索引
String indexSQL = String.format(
"CREATE UNIQUE INDEX %s %s%s%s ON %s%s%s (%s%s%s);",
tableInfo.dbType == JDBCType.MySQL ? "" : "IF NOT EXISTS",
tableInfo.quotationMarks, indexName, tableInfo.quotationMarks,
tableInfo.quotationMarks, tableInfo.tableName, tableInfo.quotationMarks,
tableInfo.quotationMarks, columnName, tableInfo.quotationMarks
);
indexSQLs.add(indexSQL);
}
}
// 表信息类
private record TableInfo(
String tableName, // 表名
String quotationMarks, // 引号或反引号
String endStr, // 表尾部信息
String primaryKey, // 主键字段
Case caseFormat, // 命名格式
JDBCType dbType // 数据库类型
) {
}
public static Future<Void> createTable(Pool pool, JDBCType type) {
Promise<Void> promise = Promise.promise();
Set<Class<?>> tableClasses = ReflectionUtil.getReflections().getTypesAnnotatedWith(Table.class);
if (tableClasses.isEmpty()) {
LOGGER.warn("Table model class not found");
promise.complete();
return promise.future();
}
List<Future<Object>> futures = new ArrayList<>();
for (Class<?> clazz : tableClasses) {
List<String> sqlList = getCreateTableSQL(clazz, type);
LOGGER.info("Class `{}` auto-generate table", clazz.getName());
for (String sql : sqlList) {
try {
pool.query(sql).execute().toCompletionStage().toCompletableFuture().join();
futures.add(Future.succeededFuture());
LOGGER.debug("Executed SQL:\n{}", sql);
} catch (Exception e) {
String message = e.getMessage();
if (message != null && message.contains("Duplicate key name")) {
LOGGER.warn("Ignoring duplicate key error: {}", message);
futures.add(Future.succeededFuture());
} else {
LOGGER.error("SQL Error: {}\nSQL: {}", message, sql);
futures.add(Future.failedFuture(e));
throw new RuntimeException(e); // Stop execution for other exceptions
}
}
}
sb.append(",\n ");
}
String sql = sb.toString();
//去掉最后一个逗号
int lastIndex = sql.lastIndexOf(",");
sql = sql.substring(0, lastIndex) + sql.substring(lastIndex + 1);
return sql.substring(0, sql.length() - 1) + endStr;
}
public static Future<Void> createTable(JDBCPool pool, JDBCType type) {
Set<Class<?>> tableClassList = ReflectionUtil.getReflections().getTypesAnnotatedWith(Table.class);
if (tableClassList.isEmpty()) LOGGER.info("Table model class not fount");
List<Future<Object>> futures = new ArrayList<>();
tableClassList.forEach(clazz -> {
String createTableSQL = getCreateTableSQL(clazz, type);
Future<Object> future = pool.query(createTableSQL).execute().compose(rs -> {
LOGGER.info("table auto generate:\n" + createTableSQL);
return Future.succeededFuture();
}).onFailure(e -> {
LOGGER.error(e.getMessage() + " SQL: \n" + createTableSQL);
});
futures.add(future);
});
Promise<Void> promise = Promise.promise();
Future.all(futures).onSuccess(r -> {
LOGGER.info("create table success");
promise.complete();
}).onFailure(promise::fail);
Future.all(futures).onSuccess(r -> promise.complete()).onFailure(promise::fail);
return promise.future();
}
}

View File

@@ -11,9 +11,6 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 初始化JDBC
* <br>Create date 2021/8/10 12:04
@@ -46,9 +43,6 @@ public class JDBCPoolInit {
if (StringUtils.isBlank(builder.dbConfig.getString("provider_class"))) {
builder.dbConfig.put("provider_class", providerClass);
}
if (StringUtils.isBlank(builder.dbConfig.getString("driverClassName"))) {
builder.dbConfig.put("driverClassName", this.type.getDriverClassName());
}
}
public static Builder builder() {
@@ -97,6 +91,8 @@ public class JDBCPoolInit {
return CreateTable.createTable(pool, type);
}
/**
* 获取连接池
*

View File

@@ -8,38 +8,21 @@ import org.apache.commons.lang3.StringUtils;
*/
public enum JDBCType {
// 添加驱动类型字段
MySQL("com.mysql.cj.jdbc.Driver", "jdbc:mysql:"),
H2DB("org.h2.Driver", "jdbc:h2:");
private final String driverClassName; // 驱动类名
MySQL("jdbc:mysql:"),
H2DB("jdbc:h2:"),
PostgreSQL("jdbc:postgresql:");
private final String urlPrefix; // JDBC URL 前缀
// 构造函数
JDBCType(String driverClassName, String urlPrefix) {
this.driverClassName = driverClassName;
JDBCType(String urlPrefix) {
this.urlPrefix = urlPrefix;
}
// 获取驱动类名
public String getDriverClassName() {
return driverClassName;
}
// 获取 JDBC URL 前缀
public String getUrlPrefix() {
return urlPrefix;
}
// 根据驱动类名获取 JDBC 类型
public static JDBCType getJDBCType(String driverClassName) {
for (JDBCType jdbcType : values()) {
if (jdbcType.getDriverClassName().equalsIgnoreCase(driverClassName)) {
return jdbcType;
}
}
throw new RuntimeException("不支持的SQL驱动类型: " + driverClassName);
}
// 根据 JDBC URL 获取 JDBC 类型
public static JDBCType getJDBCTypeByURL(String jdbcURL) {
for (JDBCType jdbcType : values()) {
@@ -47,6 +30,6 @@ public enum JDBCType {
return jdbcType;
}
}
throw new RuntimeException("不支持的SQL驱动类型: " + jdbcURL);
throw new RuntimeException("不支持的SQL类型: " + jdbcURL);
}
}