Автор оригинала: Vlad Mihalcea.
Вступление
В этой статье мы рассмотрим, как сопоставить типы хранения столбцов Oracle JSON при использовании JPA и Hibernate.
Мой hibernate-типы
проект уже давно поддерживает JSON для PostgreSQL и MySQL . Однако, разрабатывая свое новое и потрясающее Высокопроизводительное обучение SQL , я понял, что текущая реализация не обрабатывает типы столбцов Oracle JSON должным образом.
К счастью, исправление было простым, и, начиная с версии 2.7.0, теперь вы можете сохранять и извлекать атрибуты JSON в Oracle с помощью JPA и Hibernate.
Как сопоставить столбцы Oracle JSON с помощью JPA и гибернации @vlad_mihalcea https://t.co/8uJwu34okC pic.twitter.com/JuxTgIXxkK
Хранилище Oracle JSON
При использовании Oracle у вас есть два варианта сохранения объектов JSON. Вы можете использовать либо VARCHAR
столбец, либо LOB
тип хранения столбцов.
Если размер документа JSON не превышает 4000 байт, то лучше использовать тип столбца VARCHAR2(4000)
. Если размер документа JSON составляет от 4000 до 32767 байт, вместо этого можно использовать тип столбца VARCHAR2(32767)
.
Хранилище столбцов VARCHAR2(32767)
является расширенным типом данных и использует LOB
за кулисами. Первые 3500 байт хранятся внутри строки таблицы, поэтому для документов JSON, не превышающих 3500 байт, использование VARCHAR2(32767)
вместо VARCHAR2(4000) оказывает небольшое влияние на производительность. Однако для больших документов JSON хранение и извлечение документа из базового хранилища LOB будет происходить медленнее, чем чтение и запись из встроенного хранилища строк таблицы.
Для больших документов JSON BLOB предпочтительнее, чем CLUB, поскольку для последнего требуется 2 байта для хранения каждого символа, что удваивает требования к хранилищу.
Сохранение JSON в виде VARCHAR
Давайте рассмотрим, что мы разрабатываем книжный интернет-магазин, и поэтому нам нужно использовать следующую таблицу книга
база данных:
Для создания таблицы book
мы можем использовать следующую инструкцию DDL:
CREATE TABLE book ( id NUMBER(19, 0) NOT NULL PRIMARY KEY, isbn VARCHAR2(15 char), properties VARCHAR2(4000) CONSTRAINT ENSURE_JSON CHECK (properties IS JSON) )
Обратите внимание, что свойства
тип столбца VARCHAR2(4000)
и мы определили ENSURE_JSON
пользовательское ограничение, которое проверяет, хранит ли столбец свойства
правильный объект JSON.
Чтобы сопоставить таблицу book
с сущностью JPA, мы также должны решить, как обрабатывать столбец JSON. Поскольку столбец свойства
имеет тип VARCHAR
, мы можем отобразить его в виде Строки
:
@Entity(name = "Book") @Table(name = "book") @TypeDef( name = "json", typeClass = JsonType.class ) public class Book { @Id private Long id; @NaturalId private String isbn; @Type(type = "json") private String properties; public Long getId() { return id; } public Book setId(Long id) { this.id = id; return this; } public String getIsbn() { return isbn; } public Book setIsbn(String isbn) { this.isbn = isbn; return this; } public String getProperties() { return properties; } public Book setProperties(String properties) { this.properties = properties; return this; } public JsonNode getJsonNodeProperties() { return JacksonUtil.toJsonNode(properties); } }
Обратите внимание, что мы используем API в стиле Fluent для сеттеров, что позволит нам упростить процесс создания объекта.
Для получения более подробной информации об использовании построителей сущностей API в стиле Fluent ознакомьтесь с этой статьей .
Тип Json
– это универсальный тип гибернации, который работает с большинством систем реляционных баз данных и является очень удобным выбором благодаря своей переносимости.
Теперь, когда сохраняется Книга
сущность:
entityManager.persist( new Book() .setId(1L) .setIsbn("978-9730228236") .setProperties( "{" + " \"title\": \"High-Performance Java Persistence\"," + " \"author\": \"Vlad Mihalcea\"," + " \"publisher\": \"Amazon\"," + " \"price\": 44.99" + "}" ) );
Hibernate генерирует соответствующую инструкцию SQL INSERT:
INSERT INTO book ( isbn, properties, id ) VALUES ( '978-9730228236', '{ "title": "High-Performance Java Persistence", "author": "Vlad Mihalcea", "publisher": "Amazon", "price": 44.99 }', 1 )
При извлечении сущности Book
через ее естественный идентификатор мы видим , что Hibernate извлекает сущность просто отлично:
Book book = entityManager .unwrap(Session.class) .bySimpleNaturalId(Book.class) .load("978-9730228236"); assertEquals( "High-Performance Java Persistence", book.getJsonNodeProperties().get("title").asText() );
Мы также можем изменить свойство сущности JSON:
book.setProperties( "{" + " \"title\": \"High-Performance Java Persistence\"," + " \"author\": \"Vlad Mihalcea\"," + " \"publisher\": \"Amazon\"," + " \"price\": 44.99," + " \"url\": \"https://amzn.com/973022823X\"" + "}" );
И, Hibernate выдаст соответствующую инструкцию обновления SQL:
UPDATE book SET properties = '{ "title": "High-Performance Java Persistence", "author": "Vlad Mihalcea", "publisher": "Amazon", "price": 44.99, "url": "https://amzn.com/973022823X" }' WHERE id = 1
Вы не ограничены использованием атрибута String
entity. Вы также можете использовать POJO, учитывая, что свойства POJO соответствуют атрибутам JSON:
На этот раз атрибут свойства
сущности будет отображен следующим образом:
@Type(type = "json") private BookProperties properties;
Использование POJO вместо атрибута JSON на основе строк позволяет нам упростить операции чтения и записи на стороне приложения.
Обратите внимание, как хорошо мы можем создать экземпляр Книги
сущности благодаря API в стиле Fluent, используемому как сущностью, так и классом POJO:
entityManager.persist( new Book() .setId(1L) .setIsbn("978-9730228236") .setProperties( new BookProperties() .setTitle("High-Performance Java Persistence") .setAuthor("Vlad Mihalcea") .setPublisher("Amazon") .setPrice(44.99D) ) );
Изменение атрибута свойства
сущности также намного проще при использовании POJO:
Book book = entityManager .unwrap(Session.class) .bySimpleNaturalId(Book.class) .load("978-9730228236"); book.getProperties().setUrl( "https://amzn.com/973022823X" );
Операторы SQL одинаковы независимо от того, используем ли мы строку
или POJO на стороне JPA.
Хранение JSON в виде большого двоичного объекта
Если в нашей таблице book
базы данных необходимо разместить очень большие объекты JSON, то вместо этого нам нужно использовать тип столбца BLOB:
Для создания таблицы book
мы можем использовать следующую инструкцию DDL:
CREATE TABLE book ( id NUMBER(19, 0) NOT NULL PRIMARY KEY, isbn VARCHAR2(15 char), properties BLOB CONSTRAINT ENSURE_JSON CHECK (properties IS JSON) ) LOB (properties) STORE AS (CACHE)
Обратите внимание, что мы используем директиву STORE AS (КЭШ)
, которая указывает Oracle размещать страницы LOB в буферном кэше, чтобы чтение и запись выполнялись быстрее.
Как и в предыдущем случае , когда мы использовали VARCHAR
, мы можем сопоставить столбец BLOB
JSON либо с Строкой
, либо с POJO
. В обоих случаях нам нужно использовать Тип большого двоичного объекта Json
, предлагаемый проектом Hibernate Types.
Итак, при использовании атрибута сущности String
JPA сопоставление выглядит следующим образом:
@Entity(name = "Book") @Table(name = "book") @TypeDef(name = "jsonb", typeClass = JsonBlobType.class) public class Book { @Id private Long id; @NaturalId private String isbn; @Type(type = "jsonb") private String properties; //Getters, setters and utility methods omitted for brevity }
И при использовании Свойства книги
POJO сущность Книга
отображается следующим образом:
@Entity(name = "Book") @Table(name = "book") @TypeDef(name = "jsonb", typeClass = JsonBlobType.class) public class Book { @Id private Long id; @NaturalId private String isbn; @Type(type = "jsonb") private BookProperties properties; //Getters, setters and utility methods omitted for brevity }
При вставке той же сущности Book
Hibernate выполнит следующую инструкцию SQL INSERT:
INSERT INTO book ( isbn, properties, id ) VALUES ( '978-9730228236', org.hibernate.engine.jdbc.internal.BinaryStreamImpl@7d78f3d5, 1 )
При настройке столбца BLOB в Oracle Hibernate использует объект BinaryStreamImpl
, который реализует интерфейс Java InputStream
.
При изменении сущности Book
Hibernate будет использовать объект BinaryStreamImpl
для обновления столбца BLOB
:
UPDATE book SET properties = org.hibernate.engine.jdbc.internal.BinaryStreamImpl@24d61e4 WHERE id = 1
Обратите внимание, что инструкция UPDATE задает только столбец BLOB
, а не все столбцы, как в случае с механизмом обновления сущности по умолчанию.
Вывод
Проект hibernate-types
со временем был расширен для размещения широкого спектра типов баз данных, а также для поддержки нескольких систем реляционных баз данных.
С выпуском 2.7.0 типы Hibernate теперь могут сопоставлять столбцы JSON с сущностями JPA во всех 4 ведущих системах баз данных: Oracle, MySQL, SQL Server и PostgreSQL.