无法使用Oracle检索Hibernate中最后一个插入行的ID



我正在使用Hibernate工具3.2.1.GA,带有弹簧版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:此类的IDS 必须在调用保存之前手动分配():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
/ 

必须在调用save()''save()'之前手动分配此类的异常ID,这意味着您正在使用'apanded'的标识符生成策略。

分配 让应用程序在调用保存()之前将标识符分配给对象。如果未指定元素,这是默认策略。

如果您没有定义任何策略,则将默认为"分配"。"分配"策略意味着Hibernate期望该应用程序提供其自己的ID。

如果要在Oracle中使用序列ID Generator,则可以使用以下配置进行 -

如果您使用的是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会拨打以下SQL致电Oracle,检索ID并将其设置在Country对象中。

select Country_Id_Seq.nextVal from dual;

触发器的问题

由于您使用触发器在插入行时会增加ID,因此这会导致冬眠序列的问题。Hibernate使用该序列生成ID,并且数据库使用触发器来增加ID。这导致ID被增加两次。

您有三个选择来解决此问题。

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

  2. 如果您仍然需要触发器,因为该表可以在应用程序之外更新,则可以更新触发器,以便仅当未在insert语句中未设置ID时生成IDOracle触发器的Hibernate问题,用于从序列生成ID

  3. 创建一个自定义ID生成器,该生成器使用触发器将ID设置为数据,然后将其保存到DB。查看以下链接-https://forum.hibernate.org/viewtopic.php?t=973262

如果值将序列生成的ID列中的值,则应将该序列与实体定义中的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());  

最新更新