核心实现:
// 列定义
SqlNode columnDef = SqlDdlNodes.column(
SqlParserPos.ZERO,
columnName,
dataTypeSpec,
defaultValue,
defaultValue == null ? null : ColumnStrategy.DEFAULT
);
备注,这个地方卡了我三个小时以上,所有ai给出的示例最后一个参数都是传入的null,
一直报空指针异常:
java.lang.NullPointerException
	at org.apache.calcite.sql.ddl.SqlColumnDeclaration.unparse(SqlColumnDeclaration.java:77)
直到某段AI的分解过程中出现:
Calcite SqlColumnDeclaration 类中 unparse 方法的实现细节
SqlColumnDeclaration 类中的 unparse 方法的具体实现细节并未直接在任何证据中提及。
但是,可以基于其他 SqlNode 类的 unparse 方法实现进行推测。
通常,unparse 方法负责将 SQL 节点转换回 SQL 字符串。
以下是一个可能的实现示例,基于其他 SqlNode 类的 unparse 方法实现:
@Override public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { // 输出列名 name.unparse(writer, leftPrec, rightPrec); // 输出数据类型 type.unparse(writer, leftPrec, rightPrec); // 如果有默认值,则输出默认值 if (defaultValue != null) { writer.keyword("DEFAULT"); defaultValue.unparse(writer, leftPrec, rightPrec); } // 如果有约束,则输出约束 if (constraints != null) { for (SqlNode constraint : constraints) { constraint.unparse(writer, leftPrec, rightPrec); } } }
我于是去翻源码,看到以下内容,源码第45行:
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) { this.name.unparse(writer, 0, 0); this.dataType.unparse(writer, 0, 0); if (Boolean.FALSE.equals(this.dataType.getNullable())) { writer.keyword("NOT NULL"); } SqlNode expression = this.expression; if (expression != null) { switch (this.strategy) { case VIRTUAL: case STORED: writer.keyword("AS"); exp(writer, expression); writer.keyword(this.strategy.name()); break; case DEFAULT: writer.keyword("DEFAULT"); exp(writer, expression); break; default: throw new AssertionError("unexpected: " + this.strategy); } } }
反应过来,如果我想要设置默认值,那么第五个参数
ColumnStrategy 就必须传入
ColumnStrategy.DEFAULT
完整代码如下:
import org.apache.calcite.schema.ColumnStrategy; import org.apache.calcite.sql.*; import org.apache.calcite.sql.ddl.SqlColumnDeclaration; import org.apache.calcite.sql.ddl.SqlCreateTable; import org.apache.calcite.sql.dialect.PostgresqlSqlDialect; import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.pretty.SqlPrettyWriter; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.util.SqlBuilder; import org.apache.calcite.sql.ddl.SqlDdlNodes; import java.util.List; public class DdlSqlGenerator { /** * 生成 CREATE TABLE 语句 * * @param tableSchema 表模式(如 "public") * @param tableName 表名 * @param columns 列定义列表 * @return 生成的 SQL 字符串 */ public static String generateCreateTableSql(SqlDialect dialect, String tableSchema, String tableName, List<Column> columns) { // 创建表标识符(schema.tableName) SqlIdentifier tableIdentifier = new SqlIdentifier(tableName, SqlParserPos.ZERO); // 创建列定义列表 SqlNodeList columnList = new SqlNodeList(SqlParserPos.ZERO); for (Column column : columns) { // 列名 SqlIdentifier columnName = new SqlIdentifier(column.getColumnName(), SqlParserPos.ZERO); // 数据类型 SqlDataTypeSpec dataTypeSpec = createDataTypeSpec(column); SqlNode defaultValue = createDefaultValue(column.getDataDefault()); // 列定义 SqlNode columnDef = SqlDdlNodes.column( SqlParserPos.ZERO, columnName, dataTypeSpec, defaultValue, defaultValue == null ? null : ColumnStrategy.DEFAULT ); columnList.add(columnDef); } // 创建 CREATE TABLE 语句 SqlCreateTable createTable = SqlDdlNodes.createTable( SqlParserPos.ZERO, // 位置 false, // 是否替换(ifNotExists) false, // 是否临时表(isTemporary) tableIdentifier, // 表名 columnList, // 列定义 null // 查询(这里无) ); return createTable.toSqlString(dialect).getSql(); } /** * 根据 Column 属性创建数据类型规格 * * @param column 列对象 * @return 数据类型规格 */ private static SqlDataTypeSpec createDataTypeSpec(Column column) { SqlTypeName typeName = column.getDataType(); SqlDataTypeSpec dataTypeSpec; // 根据数据类型处理长度、精度和标度 switch (typeName) { case CHAR: case VARCHAR: // 有长度的数据类型 dataTypeSpec = new SqlDataTypeSpec( new SqlBasicTypeNameSpec(typeName, (int) column.getDataLength(), SqlParserPos.ZERO), SqlParserPos.ZERO ); break; case DECIMAL: // 有精度和标度的数据类型 dataTypeSpec = new SqlDataTypeSpec( new SqlBasicTypeNameSpec(typeName, column.getDataPrecision(), column.getDataScale(), SqlParserPos.ZERO), SqlParserPos.ZERO ); break; default: // 无需长度、精度的数据类型 dataTypeSpec = new SqlDataTypeSpec( new SqlBasicTypeNameSpec(typeName, SqlParserPos.ZERO), SqlParserPos.ZERO ); break; } // 设置可空性 dataTypeSpec = dataTypeSpec.withNullable(column.isNullable()); return dataTypeSpec; } //创建默认值 public static SqlNode createDefaultValue(Object defaultObj) { if (defaultObj == null) { return null; } try { if (defaultObj instanceof Number) { // 数字类型(整数、浮点数) return SqlLiteral.createExactNumeric(defaultObj.toString(), SqlParserPos.ZERO); } else if (defaultObj instanceof Boolean) { // 布尔类型 return SqlLiteral.createBoolean((Boolean) defaultObj, SqlParserPos.ZERO); } else if (defaultObj instanceof String) { // 字符串类型 // 添加额外的空字符串检查 String strValue = (String) defaultObj; if (strValue.isEmpty()) { return SqlLiteral.createCharString("", SqlParserPos.ZERO); } return SqlLiteral.createCharString(strValue, SqlParserPos.ZERO); } else { // 其他类型默认转换为字符串 return SqlLiteral.createCharString(defaultObj.toString(), SqlParserPos.ZERO); } } catch (Exception e) { // 如果创建默认值失败,返回null或记录日志 System.err.println("Failed to create default value: " + e.getMessage()); return null; } } }
@Data @AllArgsConstructor @NoArgsConstructor public class Column { private String tableSchema; private String tableName; private String columnName; private SqlTypeName dataType; private long dataLength; private int dataPrecision; private int dataScale; private boolean nullable; private Object dataDefault; private int columnId; private String columnComment; }
测试代码:
@Test
void testGenerateCreateTableSql() {
// 示例列定义
List<Column> columns = new ArrayList<>();
columns.add(new Column("public", "users", "id", SqlTypeName.BIGINT, 0, 0, 0, false, 11, 1, "Primary key"));
columns.add(new Column("public", "users", "name", SqlTypeName.VARCHAR, 50, 0, 0, true, "unknown", 2, "User name"));
columns.add(new Column("public", "users", "balance", SqlTypeName.DECIMAL, 0, 10, 2, true, 40, 3, "User balance"));
// 生成 SQL
String sql = DdlSqlGenerator.generateCreateTableSql(MysqlSqlDialect.DEFAULT,"public", "users", columns);
System.out.println(sql);
sql = DdlSqlGenerator.generateCreateTableSql(PostgresqlSqlDialect.DEFAULT,"public", "users", columns);
System.out.println(sql);
sql = DdlSqlGenerator.generateCreateTableSql(OracleSqlDialect.DEFAULT,"public", "users", columns);
System.out.println(sql);
sql = DdlSqlGenerator.generateCreateTableSql(MssqlSqlDialect.DEFAULT,"public", "users", columns);
System.out.println(sql);
}
输出结果:
CREATE TABLE `users` (`id` BIGINT NOT NULL DEFAULT (11), `name` VARCHAR(50) DEFAULT ('unknown'), `balance` DECIMAL(10, 2) DEFAULT (40)) CREATE TABLE "users" ("id" BIGINT NOT NULL DEFAULT (11), "name" VARCHAR(50) DEFAULT ('unknown'), "balance" DECIMAL(10, 2) DEFAULT (40)) CREATE TABLE "users" ("id" BIGINT NOT NULL DEFAULT (11), "name" VARCHAR(50) DEFAULT ('unknown'), "balance" DECIMAL(10, 2) DEFAULT (40)) CREATE TABLE [users] ([id] BIGINT NOT NULL DEFAULT (11), [name] VARCHAR(50) DEFAULT ('unknown'), [balance] DECIMAL(10, 2) DEFAULT (40))
                    
                
                
            
        
浙公网安备 33010602011771号