Я использую 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>
Почему в одном случае оно применимо правильно, а в другом нет?