我有一个带有不同枚举的类,例如:
class EligibilityRule{
ProductEligibility productEligibility;
CountryEligibility countryEligibility
}
enum ProductEligibility{
PRODUCT_X,
PRODUCT_Y
}
enum CountryEligibility{
US,
IN,
CN
..
}
我想将这些不同的枚举类名及其值存储到名为eligibility rule
的数据库表中,并且我的表架构看起来像这样,
String id => auto_increment id
String ruleType => enum class name (ex: ProductEligibility)
String ruleValue => enum value (ex: PRODUCT_X)
我使用的是JOOQ,过去我曾经使用强制类型来存储枚举值。但是,在这种情况下,我想存储枚举类名称和枚举值。当我从db查询记录时,我也想重建枚举对象。
我可以遵循任何模式吗?JOOQ是否可以扩展以解决此问题?
答案 0 :(得分:2)
JOOQ支持custom data types。这意味着您可以为数据库字段定义一个converter
,然后在加载时自动将其映射到您的自定义类型。数据库字段仍然是String
,但是为该字段生成的Record
将包含Field<EligibilityRule>
。这意味着您不必显式存储类名。
为此,您必须在代码生成器中注册转换器(从上面的文档页面获取):
<database>
<!-- Then, associate custom types with database columns -->
<forcedTypes>
<forcedType>
<!-- Specify the Java type of your custom type. This corresponds to the Converter's <U> type. -->
<userType>java.util.GregorianCalendar</userType>
<!-- Associate that custom type with your converter. -->
<converter>com.example.CalendarConverter</converter>
<!-- Add a Java regular expression matching fully-qualified columns. Use the pipe to separate several expressions.
If provided, both "expressions" and "types" must match. -->
<expression>.*\.DATE_OF_.*</expression>
<!-- Add a Java regular expression matching data types to be forced to
have this type.
Data types may be reported by your database as:
- NUMBER regexp suggestion: NUMBER
- NUMBER(5) regexp suggestion: NUMBER\(5\)
- NUMBER(5, 2) regexp suggestion: NUMBER\(5,\s*2\)
- any other form
It is thus recommended to use defensive regexes for types.
If provided, both "expressions" and "types" must match. -->
<types>.*</types>
</forcedType>
</forcedTypes>
</database>
另请参阅JOOQ支持的custom data type binding。
我明白您的意思了,在我的情况下,我不是为每个枚举创建新的数据库字段,而是将它们存储为rule_type和rule_value(key / value)对,其中键是类名, value是枚举值。
我明白了。不幸的是,我认为不可能以类型安全的方式执行此操作。我相信this的答案几乎就是您要问的-基于列值的特殊类型的绑定。
但是,由于您使用的是枚举,因此不能像上面的答案中那样使它们扩展超类(因为所有Enum都隐式扩展java.lang.Enum
,并且Java不支持多重继承)。但是,您可能会尝试稍微重构代码,并让所有枚举实现某种接口,即:
enum ProductEligibility implements Rule {...};
enum CountryEligibility implements Rule {...};
Rule
为:
interface Rule { String getRuleType(); String getRuleValue(); }
然后像在docs页面中的示例或单独的链接答案中那样创建一个转换器。
当然,这也意味着您的Record
中将有一个Field<Rule>
字段,而不是枚举的特定类型。如果这对您来说可以接受,那么这可能是一种可行的方法。
我没有得到这部分,
Of course, this also means that your Records would have a Field<Rule> field in them, not the specific type of the enum.
。这是否意味着我仍将用CustomConverter在db中存储两个字段,rule_type和rule_value?
不。您仍然只有一个Converter<Object, Rule>
类型的转换器。此转换器将返回 ProductEligibility
或CountryEligibility
,但不能同时返回 。
因此,如果您的数据库表具有以下内容:
eligiblity_rules
------------------
id user type value
234 223 com.foo.bar.ProductEligibility PRODUCT_Y
856 855 com.foo.bar.CountryEligibility US
您的转换器看起来像这样:
public Converter<Object, Rule> converter() {
return new Converter<Object, Rule>() {
@Override
public Rule from(Object t) {
// t here refers to the "value" db field above
if (checkIsProductEligibilityRule())
return ProductEligibility.fromValue(...);
else
return CountryEligibility.fromValue(...)
}
// Other converter methods
};
}
因此,在基于JOOQ
的代码中,您最终将拥有:
EligibilityRuleRecord record = dslContext.selectFrom(ELIGIBILITY_RULE).where(...).fetchOne();
Rule rule = record.getValue();
然后,如果要使用特定的规则类型,则需要检查并强制转换:
if (rule instanceof ProductEligibility) {
ProductEligibility peRule = (ProductEligibility) rule;
...
}
if (rule instanceof CountryEligibility) {
CountryEligibility ceRule = (CountryEligibility) rule;
...
}
...
type
数据库字段的唯一原因是选择正确的数据。不幸的是,Java代码不知道(在编译时)类型是什么,因此每次您想知道该字段的特定类型时,都需要在运行时进行类检查。