使用由数据库触发器和序列生成的ID的Hibernate映射

4
大家好。我有一个LOG表格,使用触发器和序列的组合来创建ID,因此当我插入行时,无需指定ID,否则数据库将返回错误。然而,Hibernate声称(正确地)已指定主键。
在这种情况下,应该使用什么样的“生成器”属性?
我已经尝试过“assigned”,它说:
org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save (): it.m2sc.simulator.beans.Log

使用 "select" 命令时,hibernate要求我提供自然键(natural key),但是该表中不存在自然键,只有主键(primary key)。

org.hibernate.id.IdentifierGenerationException: no natural-id property defined; need to specify [key] in generator parameters

那是我的HBM。
<hibernate-mapping>
    <class name="it.m2sc.simulator.beans.Log" table="LOG">
        <id name="id" type="integer" column="LOG_ID" access="field">
            <generator class="select" /> 
        </id>
        <property name="date" type="date" column="LOG_DATE" access="field" />
        <property name="user" type="string" column="LOG_USER" access="field" />
        <property name="evtId" type="integer" column="EVT_ID" access="field" />
        <property name="detail" type="string" column="LOG_DETAIL" access="field" />
        <property name="deleted" type="character" column="LOG_DELETED" access="field" />
        <property name="codiceRaggruppamento" column="LOG_CODICE_RAGGRUPPAMENTO" type="string" access="field" />
    </class>
</hibernate-mapping>

这个类

public class Log {
    private Integer id;
    private Date date;
    private String user;
    private Integer evtId;
    private String detail;
    private Character deleted = '0';
    private String codiceRaggruppamento;

    ... ( getter & setter )
}

表/触发器/序列的DDL

CREATE TABLE
    LOG
    (
        LOG_ID NUMBER(12) NOT NULL,
        LOG_DATE TIMESTAMP(6),
        LOG_USER VARCHAR2(50),
        EVT_ID NUMBER(12),
        LOG_DETAIL VARCHAR2(100),
        LOG_DELETED CHAR(1) DEFAULT '0 ' NOT NULL,
        LOG_CODICE_RAGGRUPPAMENTO NCHAR(2) NOT NULL,
        CONSTRAINT LOG_PK PRIMARY KEY (LOG_ID),
        CONSTRAINT LOG_CFG_EVENT_TYPE_FK1 FOREIGN KEY (EVT_ID)
        REFERENCES CFG_EVENT_TYPE (EVT_ID)
    );

   CREATE SEQUENCE  LOG_SEQ  MINVALUE 1 MAXVALUE 999999999999999999999999999 INCREMENT BY 1 START WITH 358 CACHE 20 NOORDER  NOCYCLE;

CREATE OR REPLACE TRIGGER "DTCUSR"."LOG_TRG" BEFORE INSERT ON LOG
FOR EACH ROW
BEGIN
  <<COLUMN_SEQUENCES>>
  BEGIN
    IF :NEW.LOG_ID IS NULL THEN
      SELECT LOG_SEQ.NEXTVAL INTO :NEW.LOG_ID FROM DUAL;
    END IF;
  END COLUMN_SEQUENCES;
END;

啊,只是提醒一下:db 是 Oracle11。
谢谢您的建议。

1
尝试使用“native”,但它仍然显示:“select hibernate_sequence.nextval from dual” :( - Stefano R.
1
你找到任何解决方案了吗?我也遇到了同样的问题,而且我不能在Oracle中使用序列值生成。我需要使用触发器。 - dpelisek
5个回答

1

那对我也起作用。但是在我升级了ojdbc.jar之后,它出了问题。 - Igor Donin
GenerationType.IDENTITY 创建了“生成为标识”(在Oracle中),这不允许您手动输入ID(因此无法使用触发器),这是问题所要求的。 - dpelisek

0

看看这个链接,它解释了如何使用触发器生成器。

另外,请记得在您的Hibernate持久化配置中使用hibernate.jdbc.use_get_generated_keys属性。

<property name="hibernate.jdbc.use_get_generated_keys" value="true" />

在使用链接中描述的类进行插入时,可能会出现NullPointerException。将executeAndExtract(PreparedStatement insert, SessionImplementor session)方法更改为以下任何内容:

    @Override
    protected Serializable executeAndExtract(PreparedStatement insert, SessionImplementor session)  throws SQLException {

        insert.executeUpdate();

        try {

            ResultSet rs = insert.getGeneratedKeys();
            rs.next();

            return rs.getLong(1);

        } catch (RuntimeException rt) {
            throw new SQLException("Error while attempt to use the CustomTriggerGenerator. Check the <property name=\"hibernate.jdbc.use_get_generated_keys\" value=\"true\" /> in your persistence.xml");      
        }

    }

0

sequence-identity id生成策略将序列调用直接嵌入插入语句中:

insert into log (log_id, log_date, log_user, ...)
values (LOG_SEQ.nextval, ?, ?, ...)

使用这种方式,您可以摆脱触发器,因为它不是必需的。

此外,您必须将hibernate.jdbc.use_get_generated_keys属性设置为true,以便Hibernate在执行插入语句后读取生成的键。

在hbm.xml文件中:

<id name="id" type="integer" column="LOG_ID" access="field">
  <generator class="sequence-identity" >
    <param name="sequence">LOG_SEQ</param>
  </generator>
</id>

带有注释:

@Entity
@Table(name = "LOG")
public class Log {
  @Id
  @org.hibernate.annotations.GenericGenerator(name="logSequenceGenerator", strategy = "sequence-identity",
    parameters = {@org.hibernate.annotations.Parameter(name="sequence", value="LOG_SEQ")})
  @GeneratedValue(generator = "logSequenceGenerator")
  @Column(name = "LOG_ID")
  private Integer id;
  ...
}

问题在于我无法摆脱这个触发器,这个数据库是由另一个同事创建的,我必须使用现有的内容进行工作。 - Stefano R.

-1

如果您希望仅在数据库中生成您的ID,并由Hibernate读取,您可以在@Column注释中使用“insertable”和“updatable”参数:

@Column(name = "LOG_ID", insertable=false, updatable=false)
private Integer id;

使用这个参数,Hibernate 将不会在插入和更新操作中添加 id 列。


错误。使用@Id不起作用。我有“@Id @Column(name = "ADH_ID", insertable = false, updatable = false) private Long id;”,但仍然会出现异常:“org.hibernate.id.IdentifierGenerationException:在调用save()之前必须手动分配此类的ID”。 - maxxyme

-1

1
我看到了这个参考资料,因为它在StackOverflow上被其他话题引用,但不幸的是我没有找到我的问题的答案。我写了这篇文章非常详细,希望能找到更多的帮助。 - Stefano R.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接