Перечисления Java, enums JPA и Postgres – Как мне заставить их работать вместе?
У нас есть firebase database postgres с postgres enums. Мы начинаем строить JPA в нашем приложении. У нас также есть enums Java, которые отражают enums postgres. Теперь большой вопрос: как получить JPA, чтобы понять Java enums с одной стороны и postgres enums – с другой? Сторона Java должна быть довольно простой, но я не уверен, как сделать postgres.
- Как добавить столбец, если он не существует на PostgreSQL?
- Как читать все строки из огромной таблицы?
- Может ли PostgreSQL ограничить уникальность элементов массива?
- Разрешение отклонено для отношения
- Драйвер JDBC PostgreSQL с Android
- Новые данные, не сохраняющиеся в столбце массива Rails в Postgres
- Почему PostgreSQL выполняет последовательное сканирование индексированного столбца?
- Создание временных рядов между двумя датами в PostgreSQL
Это включает в себя создание нескольких сопоставлений.
Во-первых, перечисление Postgres возвращается драйвером JDBC как экземпляр типа PGObject. Свойство type этого имени имеет имя вашего enums postgres, а значение value – его значение. (Однако порядковый номер не сохраняется, поэтому технически это не переименование и, возможно, совершенно бесполезно из-за этого)
В любом случае, если у вас есть такое определение в Postgres:
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
Затем набор результатов будет содержать PGObject с типом «настроение» и значением «happy» для столбца, имеющего этот тип enums, и строки со значением «happy».
Следующее, что нужно сделать, это написать код перехватчика, который находится между местом, где JPA читает из исходного набора результатов и устанавливает значение для вашей сущности. Например, предположим, что на Java есть следующее лицо:
public @Entity class Person { public static enum Mood {sad, ok, happy} @Id Long ID; Mood mood; }
public @Entity class Person { public static enum Mood {sad, ok, happy} @Id Long ID; Mood mood; }
К сожалению, JPA не предлагает простой точки перехвата, где вы можете выполнить преобразование из PGObject в Java enum Mood. Тем не менее, большинство поставщиков JPA имеют некоторую проприетарную поддержку для этого. Например, Hibernate имеет annotations TypeDef и Type для этого (из Hibernate-annotations.jar).
@TypeDef(name="myEnumConverter", typeClass=MyEnumConverter.class) public @Entity class Person { public static enum Mood {sad, ok, happy} @Id Long ID; @Type(type="myEnumConverter") Mood mood;
@TypeDef(name="myEnumConverter", typeClass=MyEnumConverter.class) public @Entity class Person { public static enum Mood {sad, ok, happy} @Id Long ID; @Type(type="myEnumConverter") Mood mood;
Они позволяют вам предоставить экземпляр UserType (из Hibernate-core.jar), который выполняет фактическое преобразование:
public class MyEnumConverter implements UserType { private static final int[] SQL_TYPES = new int[]{Types.OTHER}; public Object nullSafeGet(ResultSet arg0, String[] arg1, Object arg2) throws HibernateException, SQLException { Object pgObject = arg0.getObject(X); // X is the column containing the enum try { Method valueMethod = pgObject.getClass().getMethod("getValue"); String value = (String)valueMethod.invoke(pgObject); return Mood.valueOf(value); } catch (Exception e) { e.printStackTrace(); } return null; } public int[] sqlTypes() { return SQL_TYPES; } // Rest of methods omitted }
public class MyEnumConverter implements UserType { private static final int[] SQL_TYPES = new int[]{Types.OTHER}; public Object nullSafeGet(ResultSet arg0, String[] arg1, Object arg2) throws HibernateException, SQLException { Object pgObject = arg0.getObject(X); // X is the column containing the enum try { Method valueMethod = pgObject.getClass().getMethod("getValue"); String value = (String)valueMethod.invoke(pgObject); return Mood.valueOf(value); } catch (Exception e) { e.printStackTrace(); } return null; } public int[] sqlTypes() { return SQL_TYPES; } // Rest of methods omitted }
Это не полное рабочее решение, а просто быстрый указатель в надежде на правильное направление.
Я фактически использовал более простой способ, чем тот, что с PGObject и Converters. Так как в Postgres enums преобразуются вполне естественно в текст, вам просто нужно позволить ему делать то, что он делает лучше всего. Я возьму пример Арджана настроений, если он не возражает:
Тип enums в Postgres:
CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
Класс и перечисление в Java:
public @Entity class Person { public static enum Mood {sad, ok, happy}; @Enumerated(EnumType.STRING) Mood mood;
}
Этот тег @Enumerated указывает, что сериализация / десериализация enums должна выполняться в тексте. Без него он использует int, что более сложно, чем что-либо.
На этом этапе у вас есть два варианта. Вы либо:
- Добавьте stringtype = unspecified в строку подключения, как описано в параметрах соединения JDBC . Это позволяет Postgres угадывать правый тип и полностью преобразовывать все, так как он получает что-то вроде «enum = unknown», которое является выражением, которое он уже знает, что делать (передать значение? В десериализатор левого типа). Это предпочтительный вариант, так как он должен работать для всех простых UDT, таких как enums за один раз.
Или:
-
Создайте неявное преобразование из varchar в enum в базе данных. Таким образом, в этом втором случае firebase database получает некоторое присвоение или сравнение, например «enum = varchar», и находит в своем внутреннем каталоге правило о том, что оно может передавать правое значение через функцию сериализации varchar, за которой следует функция десериализации ENUM. Это больше шагов, чем нужно; и наличие слишком большого количества неявных бросков в каталоге может привести к неоднозначным интерпретациям произвольных запросов, поэтому используйте их экономно. Создание роли:
СОЗДАЙТЕ CAST (ХАРАКТЕР ВРАЩАЮТСЯ как настроение), НЕ НЕЗАВИСИМО ОТ КАКОЙ-ЛИБО;
Должен работать именно так.
Я подал сообщение об ошибке с патчем, включенным в Hibernate: HHH-5188
Патч работает для меня, чтобы прочитать перечисление PostgreSQL в перечисление Java, используя JPA.