Nano Hash - криптовалюты, майнинг, программирование

Почему Hibernate Envers не учитывает тип столбца в исходной таблице?

Я использую Hibernate 5.2.9.Final (версия как ядра, так и Envers). При добавлении аудита в таблицу, созданную с помощью:

CREATE TABLE Transaction (
    id                      BIGINT PRIMARY KEY
   ,name                    VARCHAR(120) NOT NULL
   ,currency                CHAR(3) REFERENCES Currency(code)
   ,country                 VARCHAR(3)
   ,status                  INTEGER
   ,description             VARCHAR(1000)
   ,amount                  NUMERIC(30,5)
   ,closingDate             TIMESTAMP WITH TIME ZONE
   ,rangeStart              TIMESTAMP WITH TIME ZONE
   ,rangeEnd                TIMESTAMP WITH TIME ZONE
   ,isin                    VARCHAR(12)
   ,version                 INTEGER NOT NULL DEFAULT 0
);

Hibernate Envers создал таблицу _AUD и выдал следующее:

CREATE TABLE transaction_aud (
    id bigint NOT NULL,
    rev integer NOT NULL,
    revtype smallint,
    closingdate timestamp without time zone,
    closingdate_mod boolean,
    country character varying(255),
    country_mod boolean,
    currency character varying(255),
    currency_mod boolean,
    description character varying(255),
    description_mod boolean,
    amount numeric(19,2),
    isin character varying(255),
    isin_mod boolean,
    name character varying(255),
    name_mod boolean,
    rangeend timestamp without time zone,
    rangeend_mod boolean,
    rangestart timestamp without time zone,
    rangestart_mod boolean,
    status integer,
    status_mod boolean
);

Файл сопоставления сущностей:

<entity name="Transaction" class="com.project.datamodel.TransactionTO" access="FIELD">
    <attributes>
        <id name="id" access="PROPERTY">
            <generated-value strategy="SEQUENCE" generator="TransactionIdSeq" />
            <sequence-generator name="TransactionIdSeq" sequence-name="TransactionIdSeq" allocation-size="1" />
        </id>
        <basic name="name" optional="false"> <column length="120" /> </basic>
        <basic name="currency"> <column columnDefinition="CHAR(3)" /> </basic>
        <basic name="country"> <column length="3" /> </basic>
        <basic name="status" />
        <basic name="description"> <column length="1000" /> </basic>
        <basic name="amount"> <column columnDefinition="NUMERIC(30,5)" /> </basic>
        <basic name="closingDate"> <column columnDefinition="TIMESTAMP WITH TIME ZONE" /> </basic>
        <basic name="rangeStart"> <column columnDefinition="TIMESTAMP WITH TIME ZONE" /> </basic>
        <basic name="rangeEnd"> <column columnDefinition="TIMESTAMP WITH TIME ZONE" /> </basic>
        <basic name="isin"> <column length="12" /> </basic>
        <transient name="favorite" />
        <version name="version" />
    </attributes>
</entity>

ПРИМЕЧАНИЕ. Я удалил все связанные many-to-one отношения из объекта, поскольку они просто добавляют объем, не затрагивая мой основной вопрос.

Проблемы:

VARCHAR/CHAR тип и длина полей игнорируются

ВСЕ поля CHAR и VARCHAR, независимо от их длины, были преобразованы в поля VARCHAR(255)/character varying(255).

Это вызвало проблему при сохранении данных в поле description, которое обычно должно содержать 1000 символов. После превышения предельного размера 255 механизм базы данных выдал исключение, поскольку данные были слишком широкими для размера столбца.

Кроме того, какой смысл преобразовывать CHAR(3) в VARCHAR(255)? Мне это кажется ужасно расточительным, но я надеюсь, что есть какая-то другая причина, о которой я не знаю.

TIMESTAMP атрибут часового пояса игнорируется

Поля с типом TIMESTAMP были изменены с явного определения С часовым поясом на неопределенный часовой пояс (я знаю, что тип TIMESTAMP в PostgreSQL, который является моим базовым движком базы данных, определил их без часовой пояс по умолчанию).

Тип числовой точности изменен

Поле суммы, определенное как NUMERIC (30,5), было определено в таблице аудита как NUMERIC(19,2).

Почему Hibernate Envers не учитывает типы столбцов исходной таблицы при создании таблицы _AUD?

Исключительный случай

Я также обнаружил, что он учитывает размер в случае одной таблицы: LocalizedMessage

<entity name="LocalizedMessage" class="com.project.datamodel.to.LocalizedMessageTO" access="FIELD">
        <attributes>
                <id name="id" access="PROPERTY">
                        <generated-value strategy="SEQUENCE" generator="LocalizedMessageIdSeq" />
                        <sequence-generator name="LocalizedMessageIdSeq" sequence-name="LocalizedMessageIdSeq" allocation-size="1" />
                </id>
                <many-to-one name="adminMessage"> <join-column name="adminMessageId" /> </many-to-one>
                <basic name="localeCode"> <column length="7" /> </basic>
                <basic name="message"> <column length="500" /> </basic>
                <version name="version" />
        </attributes>
</entity>

Исходное определение таблицы:

CREATE TABLE LocalizedMessage (
    id             BIGINT PRIMARY KEY
   ,adminMessageId BIGINT REFERENCES AdminMessage(id)
   ,localeCode     VARCHAR(7)
   ,message        VARCHAR(500)
   ,version        INTEGER NOT NULL DEFAULT 0
);

И сгенерированная таблица _AUD была:

CREATE TABLE localizedmessage_aud (
    id bigint NOT NULL,
    rev integer NOT NULL,
    revtype smallint,
    localecode character varying(7),
    localecode_mod boolean,
    message character varying(500),
    message_mod boolean,
    adminmessageid bigint,
    adminmessage_mod boolean
);

Определения файлов сопоставления Envers

ПРИМЕЧАНИЕ. Это было сделано ПОСЛЕ добавления директив длины в файл сопоставления. Единственными проблемными полями теперь являются поля TIMESTAMP WITH TIME ZONE и NUMERIC (в которых изменена точность), а также поля CHAR(). По сути, атрибут columnDefinition XML-файла сопоставления JPA не соблюдается.

Включение ведения журнала трассировки для org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl привело к созданию записей в журнале, подобных следующим:

2017-06-10 15:52:19,981 TRACE org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl ~ ------------------------------------------------------------
2017-06-10 15:52:19,984 TRACE org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl ~ Envers-generate entity mapping -----------------------------
<hibernate-mapping auto-import="false">
 <class entity-name="com.project.datamodel.to.TransactionTO_AUD" discriminator-value="Transaction" table="Transaction_AUD" schema="app" abstract="false">
  <composite-id name="originalId">
   <key-property name="id" type="long">
    <column name="id" length="255" scale="2" precision="19"/>
   </key-property>
   <key-many-to-one type="integer" class="com.project.datamodel.to.CustomRevisionEntity" name="REV">
    <column name="REV"/>
   </key-many-to-one>
  </composite-id>
  <property insert="true" update="false" name="REVTYPE" type="org.hibernate.envers.internal.entities.RevisionTypeType"/>
  <property insert="true" update="false" name="closingDate" type="timestamp">
   <column name="closingDate" length="255" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="closingDate_MOD" type="boolean"/>
  <property insert="true" update="false" name="country" type="string">
   <column name="country" length="3" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="country_MOD" type="boolean"/>
  <property insert="true" update="false" name="currency" type="string">
   <column name="currency" length="255" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="currency_MOD" type="boolean"/>
  <property insert="true" update="false" name="description" type="string">
   <column name="description" length="1000" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="description_MOD" type="boolean"/>
  <property insert="true" update="false" name="amount" type="big_decimal">
   <column name="amount" length="255" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="amount_MOD" type="boolean"/>
  <property insert="true" update="false" name="isin" type="string">
   <column name="isin" length="12" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="isin_MOD" type="boolean"/>
  <property insert="true" update="false" name="name" type="string">
   <column name="name" length="120" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="name_MOD" type="boolean"/>
  <property insert="true" update="false" name="rangeEnd" type="timestamp">
   <column name="rangeEnd" length="255" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="rangeEnd_MOD" type="boolean"/>
  <property insert="true" update="false" name="rangeStart" type="timestamp">
   <column name="rangeStart" length="255" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="rangeStart_MOD" type="boolean"/>
  <property insert="true" update="false" name="status" type="converted::com.project.datamodel.converter.StatusConverter">
   <column name="status" length="255" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="status_MOD" type="boolean"/>
 </class>
</hibernate-mapping>

И для таблицы LocalizedMessage:

2017-06-10 15:52:19,947 TRACE org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl ~ ------------------------------------------------------------
2017-06-10 15:52:19,949 TRACE org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl ~ Envers-generate entity mapping -----------------------------
<?xml version="1.0" encoding="UTF-8"?>

<hibernate-mapping auto-import="false">
 <class entity-name="com.project.datamodel.to.LocalizedMessageTO_AUD" discriminator-value="LocalizedMessage" table="LocalizedMessage_AUD" schema="app" abstract="false">
  <composite-id name="originalId">
   <key-property name="id" type="long">
    <column name="id" length="255" scale="2" precision="19"/>
   </key-property>
   <key-many-to-one type="integer" class="com.project.datamodel.to.CustomRevisionEntity" name="REV">
    <column name="REV"/>
   </key-many-to-one>
  </composite-id>
  <property insert="true" update="false" name="REVTYPE" type="org.hibernate.envers.internal.entities.RevisionTypeType"/>
  <property insert="true" update="false" name="localeCode" type="string">
   <column name="localeCode" length="7" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="localeCode_MOD" type="boolean"/>
  <property insert="true" update="false" name="message" type="string">
   <column name="message" length="500" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="message_MOD" type="boolean"/>
  <property insert="true" update="false" name="adminMessage_id" type="long">
   <column name="adminMessageId" length="255" scale="2" precision="19"/>
  </property>
  <property insert="true" update="false" name="adminMessage_MOD" type="boolean"/>
 </class>
</hibernate-mapping>

Почему в одном случае оно применимо правильно, а в другом нет?

15.05.2017

  • Не могли бы вы предоставить свои сопоставления сущностей для сущности транзакции? Не могли бы вы также указать, какую версию Hibernate и Hibernate Envers вы используете? 15.05.2017
  • @Нарос Спасибо! Я добавил файл сопоставления XML... Я создал исходную таблицу вручную в SQL. Вы заставили меня задуматься о том, что, может быть, мне следовало добавить все эти размеры и в XML-файл сопоставления? (чтобы Энверс забрал их оттуда) Разве он не может запросить структуру таблицы в БД? 15.05.2017
  • Envers полностью основывает свои сопоставления на данных, доступных из модели сопоставления Hibernate, поэтому нет, он не смотрит на схему базы данных напрямую. Поскольку в вашем XML-сопоставлении не указаны длины столбцов, для строк, например, используется значение по умолчанию 255 JPA. Если вы укажете длину столбцов в файле сопоставления XML, Envers должен настроиться автоматически. 15.05.2017
  • @Naros Я добавил определения размеров / столбцов в файл сопоставления, но столбцы в таблице аудита все еще генерируются неправильно! Есть ли что-то, что мне не хватает где-то еще? 08.06.2017
  • Какой диалект Postgres вы используете? Мне любопытно, разрешите ли вы Hibernate и Envers создавать свои таблицы, если вы обнаружите, что один и тот же тип символьных данных используется во всех таблицах, и разница является продуктом ручного и автоматического создания с диалектом, который вы используете. Одна вещь, которую вы можете сделать, это включить ведение журнала трассировки для org.hibernate.envers.boot.internal.AdditionalJaxbMappingProducerImpl, чтобы увидеть XML, который Envers создает для каждого сопоставления объектов. Вы увидите, что Envers генерирует type="string", и это просто Hibernate, а выбор диалекта при выборе символа варьируется. 08.06.2017
  • @Naros В файле журнала написано HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect. Я не разрешаю Hibernate создавать таблицы, потому что схема базы данных управляется Flyway (который поддерживает кластеризацию, а Envers — нет). Я добавил записи журнала к вопросу. По сути, теперь все длины соблюдаются, игнорируется только атрибут columnDefinition. Возможно, мне следует указать точность/типы по-другому? 10.06.2017
  • Обновлен мой ответ, почему определения ваших столбцов не отражаются в выводе XML. TL;DR: неверный XML :) 10.06.2017

Ответы:


1

Как указано в моем комментарии, Envers никоим образом не интерпретирует свою схему из существующей схемы базы данных. Фактически, весь процесс создания модели отображения основан на проверке модели отображения времени загрузки Hibernate ORM, которую вы можете найти в org.hibernate.mapping.

Это означает, что для таких вещей, как длины столбцов, они должны предоставляться как часть вашей модели сопоставления либо через аннотации, либо в файле сопоставления XML, чтобы Envers мог генерировать свойства, которые используют те же длины или, в некоторых случаях, пользовательские определения столбцов.

Если вы укажете длины в файле сопоставления XML и заново сгенерируете схему Envers, она должна очень точно выровняться со схемой вашего объекта Hibernate.

ОБНОВЛЕНИЕ
Причина, по которой определения столбцов не отображаются в сопоставлениях Envers, заключается в том, что ваш файл JPA ORM.XML недействителен, не соответствует определению схемы и, следовательно, не только Envers не будет видеть определения ваших столбцов, а также Hibernate.

Ваши сопоставления должны быть:

<column column-definition="TIMESTAMP WITH TIME ZONE"/>

Вы заметите, что атрибут называется column-definition, а не columnDefinition.

15.05.2017
Новые материалы

Кластеризация: более глубокий взгляд
Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

Как написать эффективное резюме
Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

Частный метод Python: улучшение инкапсуляции и безопасности
Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

Как я автоматизирую тестирование с помощью Jest
Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..