2013-12-02 8 views
12

В частности, меня интересует использование PostgreSQLs json.Возможны ли типы JPA (EclipseLink)?

Ядро проблемы похоже, что внутри Eclipselink нет внутреннего отображения до json. Таким образом, используя наивный подход с:

@Column(name = "json", columnDefinition = "json") 
public String getJson() { 
    return json; 
} 

... и пытается вставить объект, я получаю исключение:

Internal Exception: org.postgresql.util.PSQLException: ERROR: column "json" is of type json but expression is of type character varying 

Fair достаточно, я полагаю.

Просматривая документы EclipseLink, кажется, что применимые настройки (Преобразования, Нативные запросы, Преобразователи) зависят от данных, состоящих из поддерживаемых сопоставлений (числа, даты, строки и т. Д.), Поэтому он делает это довольно неудобным чтобы обойти использование определенных типов поставщиков.

Основная причина, по которой это так расстраивает то, что json Тип в posgresql выражается так же, как текст/varchar, и я верю (на данный момент, но не навсегда) является просто псевдонимом этого типа - поэтому драйвер больше чем способна передать это, это просто правила проверки на моем пути.

С точки зрения решения, я не против потери переносимости (с точки зрения агностики базы данных и использования специфических для конкретного поставщика типов), но просто хочу получить решение, которое позволяет мне использовать тип json как атрибут на нормальном JPA Entity и сохранить все другое поведение, к которому он привык (генерация схемы, слияние, постоянство, транзакционный код

+0

Да, отличный вопрос! – Jmoney38

ответ

23

Прогулка по SO Я нашел много вопросов, подобных этому в отношении типов JSON или XML для отображения в Postgres. Похоже, что никто не столкнулся с проблемой чтения из пользовательского типа Postgres, поэтому здесь решение для чтения и записи использует механизм преобразования типа JPA.

Драйвер JDBC Postgres отображает все атрибуты для неизвестных (для Java) типов в объект org.postgresql.util.PGobject, поэтому достаточно сделать конвертер для этого типа.Вот пример лицо:

@Entity 
public class Course extends AbstractEntity { 
    @Column(name = "course_mapped", columnDefinition = "json") 
    @Convert(converter = CourseMappedConverter.class) 
    private CourseMapped courseMapped; // have no idea why would you use String json instead of the object to map 

    // getters and setters 
} 

Вот конвертер пример:

@Converter 
public class CourseMappedConverter implements AttributeConverter<CourseMapped, PGobject> { 
    @Override 
    public PGobject convertToDatabaseColumn(CourseMapped courseMapped) { 
     try { 
      PGobject po = new PGobject(); 
      // here we tell Postgres to use JSON as type to treat our json 
      po.setType("json"); 
      // this is Jackson already added as dependency to project, it could be any JSON marshaller 
      po.setValue((new ObjectMapper()).writeValueAsString(courseMapped)); 
      return po; 
     } catch (JsonProcessingException e) { 
      e.printStackTrace(); 
      return null; 
     } catch (SQLException e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 

    @Override 
    public CourseMapped convertToEntityAttribute(PGobject po) { 
     try { 
      return (new ObjectMapper()).readValue(po.getValue(),CourseMapped.class); 
     } catch (IOException e) { 
      e.printStackTrace(); 
      return null; 
     } 
    } 
} 

Если вам действительно нужно придерживаться представления строки JSON в вашей организации, вы можете сделать конвертер как это для типа струнного

implements AttributeConverter<String, PGobject> 

Здесь очень грязное (хотя и работающее) доказательство концепции, оно также использует сериализацию поддельного объекта, чтобы сообщить JPA, что объект был изменен, если он был

https://github.com/sasa7812/psql-cache-evict-POC

+0

Отличный пример thx для него, я проверил использование Postgres 9.3, EclipseLink 2.6.2, Postgres JDBC 9.4-1208, контейнер TomEE 1.7.0, и он отлично работал. Я пробовал также с типом данных XML, но, к сожалению, тип XML автоматически преобразовывался в String и не возвращался как PGobject при чтении из базы данных (для записи PGobject был странным для меня), поэтому в этом случае вам нужно использовать Object (с небольшим экземпляром проверки, вероятно,) не PGobject в конкретном классе AttributeConverter. –

+0

Я все еще иду эта ошибка: ОШИБКА: столбец «my_column» имеет тип json, но выражение имеет тип bytea. Любая идея почему? – Thermech

+0

Как реализовать конвертер для Generics! – madhairsilence

2

PostgreSQL слишком строг в отношении неявных отбросов между текстовыми типами. Самый простой способ - это обходной путь, создавая литье; this answer.

чистый способ сделать это было бы создание проформы JPA которое вызывает setObject(my_json), и/или научит вашего провайдера JPA явно добавлять CAST('myvalue' AS json), когда он генерирует запросы. Это боль, поскольку для этого требуются конкретные расширения провайдера JPA.

This Stack Overflow search найдет кучу связанных вопросов для типа xml, с которыми люди сталкиваются с подобными проблемами.

+0

Спасибо за ответ. Что касается 'setObject', какая функция/метод? [setObject] (http://jdbc.postgresql.org/development/privateapi/org/postgresql/jdbc3g/AbstractJdbc3gStatement.html) из драйвера jdbc PostgreSQL? – markdsievers

+0

@markdsievers Из API JDBC, на самом деле. http://docs.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html#setObject(int, java.lang.Object) –

Смежные вопросы