如何在jOOQ中实现VARCHAR类型针对SQL Server的三倍长度定制?
需求与问题
我想要创建一个和SQLDataType.VARCHAR行为完全一致的DataType,但在SQL Server环境下生成的字段长度是指定值的三倍。比如执行以下代码时:
create.createTable("table") .column("col1", myVarchar.length(15)) .execute();
- MySQL中生成
varchar(15) - SQL Server中生成
varchar(45)
我原本尝试通过继承DefaultDataType实现,但由于construct()方法和DefaultDataType的重载构造方法是包私有,无法完成:
myVarchar = new DefaultDataType<>(null, String.class, "varchar(l)"); new DefaultDataType<>(SQLDialect.MYSQL, myVarchar, "varchar", "char"); new SqlServerCharType(myVarchar); private class SqlServerCharType extends DefaultDataType<String> { public SqlServerCharType(DataType<String> baseType) { super(SQLDialect.SQLSERVER, baseType, "varchar", "varchar(max)"); } DefaultDataType<String> construct(Integer newPrecision, Integer newScale, Integer newLength, Nullability newNullability, boolean newReadonly, Generator<?, ?, String> newGeneratedAlwaysAs, QOM.GenerationOption newGenerationOption, QOM.GenerationLocation newGenerationLocation, Collation newCollation, CharacterSet newCharacterSet, boolean newIdentity, Field<String> newDefaultValue) { return new DefaultDataType(this, newPrecision, newScale, newLength * 3, newNullability, newReadonly, newGeneratedAlwaysAs, newGenerationOption, newGenerationLocation, newCollation, newCharacterSet, newIdentity, newDefaultValue); } }
请问能不能通过Binding实现这个需求?还是只能在获取连接、确定方言后,用下面这种动态调整长度的方式处理?
create.createTable("table") .column("col1", multiply(create, SQLDataType.VARCHAR(15)) .execute(); ... private <T> DataType<T> multiply(Scope scope, DataType<T> type) { if (scope.dialect().family().equals(SQLDialect.SQLSERVER) && type.isString()) { return type.length(type.length() * 3); } return type; }
解决方案
1. Binding无法实现该需求
Binding的作用是处理Java类型与数据库类型之间的数据映射(比如读写数据时的类型转换),并不负责DDL语句的生成逻辑,所以没办法通过它修改SQL Server下的字段长度定义。
2. 推荐方案:封装自定义DataType工具类
你提到的动态调整长度思路完全可行,还可以进一步封装成工具类,避免重复判断:
public class CustomDataTypes { public static DataType<String> varchar(int length, Scope scope) { if (scope.dialect().family() == SQLDialect.SQLSERVER) { return SQLDataType.VARCHAR(length * 3); } return SQLDataType.VARCHAR(length); } } // 使用方式 create.createTable("table") .column("col1", CustomDataTypes.varchar(15, create)) .execute();
3. 自定义DataType的替代思路(绕过包私有限制)
如果想完全模拟SQLDataType.VARCHAR的行为并自动适配长度,可以通过实现DataType接口,将大部分方法委托给原VARCHAR类型,仅在获取类型名称时动态调整长度:
public static DataType<String> myVarchar(int length) { return new DataType<String>() { @Override public <U> DataType<U> convert(Converter<? super String, ? extends U> converter) { return SQLDataType.VARCHAR(length).convert(converter); } @Override public DataType<String> length(int newLength) { return myVarchar(newLength); } @Override public String getTypeName(Configuration configuration) { int actualLength = configuration.dialect().family() == SQLDialect.SQLSERVER ? length * 3 : length; return SQLDataType.VARCHAR(actualLength).getTypeName(configuration); } // 剩余DataType接口方法均委托给SQLDataType.VARCHAR(length)实现 }; }
不过这种方式需要实现大量接口方法,工具类方案显然更简洁实用。
总结
优先选择封装工具类动态调整长度的方案,代码简洁易维护,完全满足需求。自定义DataType接口的方式实现成本较高,没必要舍近求远。
内容的提问来源于stack exchange,提问作者Michal Turcel
相关产品推荐
相关产品推荐

