0

我将 Hibernate Tools 3.2.1.GA 与 Spring 版本 3.0.2 一起使用。我正在尝试将最后插入的行的 id 检索到 Oracle(10g) 数据库中,如下所示。

Session session=NewHibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();

Country c=new Country();

c.setCountryId(new BigDecimal(0));
c.setCountryName(request.getParameter("txtCountryName"));
c.setCountryCode(request.getParameter("txtCountryCode"));
Zone z=(Zone) session.get(Zone.class, new BigDecimal(request.getParameter("zoneId")));
c.setZone(z);
session.save(c);

session.flush();    
System.out.println(c.getCountryId());    
session.getTransaction().commit();       

在数据被序列化到数据库之后并且在事务提交之前,该语句System.out.println(c.getCountryId());预计会返回当前插入的 id,但它不会因为前面代码片段中的以下行(因为它可能在我看来)。

c.setCountryId(new BigDecimal(0));

我不确定为什么在我的情况下需要此语句(插入时)。我在任何地方都没有看到这个声明。省略此行会导致引发以下异常。

org.hibernate.id.IdentifierGenerationException:必须在调用 save() 之前手动分配此类的 id:model.Country

c.setCountryId(new BigDecimal(0));在插入过程中真的需要这个语句吗?它是 Oracle 数据库中序列生成的主键,由于该行,此语句System.out.println(c.getCountryId());始终返回0实际预期返回当前会话中当前插入的 id。

那么,在这种情况下如何获取最后生成的 id 呢?我是不是走错了路,有不同的方法吗?


编辑:

CREATE TABLE  "COUNTRY" 
(   
    "COUNTRY_ID" NUMBER(35,0) NOT NULL ENABLE, 
    "COUNTRY_CODE" VARCHAR2(10), 
    "COUNTRY_NAME" VARCHAR2(50), 
    "ZONE_ID" NUMBER(35,0), 

    CONSTRAINT "COUNTRY_PK" PRIMARY KEY ("COUNTRY_ID") ENABLE, 
    CONSTRAINT "COUNTRY_FK" FOREIGN KEY ("ZONE_ID")
                            REFERENCES  "ZONE" ("ZONE_ID") ON DELETE CASCADE ENABLE
)
/

CREATE OR REPLACE TRIGGER  "BI_COUNTRY" 
before insert on "COUNTRY"               
for each row  

begin   
    select "COUNTRY_SEQ".nextval into :NEW.COUNTRY_ID from dual; 
end; 

/
ALTER TRIGGER  "BI_COUNTRY" ENABLE
/ 
4

2 回答 2

4

异常“必须在调用 save() 之前手动分配此类的 ids”意味着您正在使用“已分配”的标识符生成策略。

assign 让应用程序在调用 save() 之前为对象分配 一个标识符。如果未指定任何元素,这是默认策略。

如果你没有定义任何策略,hibernate 默认为'assigned'。'assigned' 策略意味着 hibernate 期望应用程序提供它自己的 id。

如果要在 Oracle 中使用序列 id 生成器,可以使用以下配置进行 -

如果您使用的是 xml -

   <id name="countryId" type="java.lang.Integer">  
        <column name="Country_Id" />  
        <generator class="sequence">  
            <param name="sequence">Country_Id_Seq</param>               
        </generator>  
    </id>

如果您使用注释 -

   @Id
   @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="Country_Id_Seq")
   @SequenceGenerator(name="Country_Id_Seq", sequenceName="Country_Id_Seq"  )
   private Integer sequence;

你的代码应该是这样的 -

Country c=new Country();

c.setCountryName(request.getParameter("txtCountryName"));
c.setCountryCode(request.getParameter("txtCountryCode"));
Zone z=(Zone) session.get(Zone.class, new BigDecimal(request.getParameter("zoneId")));
c.setZone(z);
session.save(c);

session.flush();    
System.out.println(c.getCountryId()); 

当 'session.save(c)' 执行时,hibernate 对 Oracle 进行以下 sql 调用,检索 id 并将其设置在 Country 对象中。

select Country_Id_Seq.nextVal from dual;

触发器问题

由于您在插入行时使用触发器来增加 id,这将导致休眠序列出现问题。Hibernate 使用序列来生成 id,而数据库使用触发器来增加 id。这导致 id 增加了两次。

您可以通过三个选项来解决此问题。

  1. 删除触发器,因为它不是必需的。

  2. 如果您仍然需要触发器,因为可以在应用程序外部更新表,您可以更新触发器,以便仅当未在插入语句中设置 id 时生成 id

  3. 创建一个自定义 id 生成器,该生成器使用触发器在数据保存到 db 之前设置数据中的 id。查看以下链接 - https://forum.hibernate.org/viewtopic.php?t=973262

于 2012-11-09T02:47:53.540 回答
1

如果将值放入由序列生成的 ID 列,则应将该序列与实体定义中的 ID 列相关联,以便在插入期间由 Hibernate 用 ID 值填充属性。

使用注释:

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CountryIdSequence")
@SequenceGenerator(name = "CountryIdSequence", sequenceName = "COUNTRY_ID_SEQUENCE")
@Column(name = "COUNTRY_ID")
private BigDecimal countryId;

使用 hbm:

<id name="countryId" type="big_decimal">
    <column name="COUNTRY_ID" />
    <generator class=""sequence">
        <param name="sequence">COUNTRY_ID_SEQUENCE</param>
    </generator>
</id>

然后,它将在保存后可用。

在刷新对象之前,对数据库层实体所做的任何更改都不会反映在休眠实体层中。

session.save(c);
session.flush();    
// Refresh the object for columns modified in the DB by IDENTITY / SEQUENCE / Triggers.
session.refresh(c);
System.out.println(c.getCountryId());  
于 2012-11-09T01:48:41.837 回答