Как сохранить имя класса Enum и значение enum в базе данных - PullRequest
0 голосов
/ 15 мая 2019

У меня есть класс с разными перечислениями, например:

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, в прошлом я использовал принудительный тип, чтобы просто сохранить значение перечисления.Но в этом случае я хочу сохранить имя класса enum и значение enum.Я также хочу восстановить объект enum, когда я запрашиваю записи из базы данных.

Есть ли какие-либо шаблоны, которым я могу следовать или есть какие-либо функциональные возможности в JOOQ, которые я могу расширить, чтобы решить эту проблему?

1 Ответ

2 голосов
/ 15 мая 2019

JOOQ поддерживает пользовательские типы данных . Это означает, что вы можете определить 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.


Я понимаю, что вы имеете в виду, в моем случае я не создаю новое поле базы данных для каждого перечисления, вместо этого они будут храниться в виде пары rule_type и rule_value (ключ / значение), где ключ - это имя класса и значение - это значение перечисления.

Понятно. К сожалению, я не думаю, что было бы возможно сделать это безопасным для типов способом. Я считаю, что этот ответ в значительной степени соответствует тому, что вы спрашиваете - привязка специального типа, основанная на значении столбца.

Однако, поскольку вы используете перечисления, вы не можете заставить их расширять суперкласс, как в ответе выше (поскольку все перечисления неявно расширяют java.lang.Enum, а Java не поддерживает множественное наследование). Тем не менее, вы можете попытаться немного реорганизовать свой код и сделать так, чтобы все ваши перечисления реализовывали некоторый интерфейс, то есть:

enum ProductEligibility implements Rule {...};
enum CountryEligibility implements Rule {...};

С Rule:

interface Rule { String getRuleType(); String getRuleValue(); }

А затем создайте конвертер, как в примере на странице документов или в отдельном связанном ответе.

Конечно, это также означает, что ваши 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.. Означает ли это, что я все еще буду хранить два поля в db, rule_type и rule_value с CustomConverter для каждого из них?

Нет. У вас все равно будет только один конвертер типа 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 не знает (во время компиляции), каким будет тип, поэтому вам понадобится проверка классов во время выполнения каждый раз, когда вы захотите узнать тип этого поля.

...