为什么Hibernate Envers不尊重原始表中的列类型?

时间:2017-05-15 10:48:38

标签: 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字段类型&amp;长度忽略

所有CHARVARCHAR字段,无论其长度如何,都会转换为VARCHAR(255) / character varying(255)字段。

description字段中保存通常应包含1000个字符的数据时,这会导致问题。超过255限制大小后,数据库引擎会因异常而导致异常,因为数据对于列大小来说太宽了。

此外,将CHAR(3)投射到VARCHAR(255)会有什么意义?这对我来说似乎非常浪费,但我希望还有其他一些我不知道的原因。

TIMESTAMP时区属性被忽略

TIMESTAMP - 类型字段从显式定义 WITH 更改为定义为未定义时区的时区(我知道PostgreSQL中的TIMESTAMP类型 - 这是我的后备数据库引擎 - 默认情况下没有时区定义它们。)

更改了数字精度类型

定义为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 ZONENUMERIC字段(其中精度已更改)以及CHAR()字段。 实质上,不尊重JPA XML映射文件的columnDefinition属性。

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>

为什么它适用于一种情况而不适用于另一种情况?

1 个答案:

答案 0 :(得分: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