0

Spring バージョン 3.0.2 で Hibernate Tools 3.2.1.GA を使用しています。次のように、最後に挿入された行の 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: このクラスの ID は、save() を呼び出す前に手動で割り当てる必要があります: 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

例外「このクラスの ID は、save() を呼び出す前に手動で割り当てる必要があります」は、「割り当て済み」の識別子生成戦略を使用していることを意味します。

assigned を使用すると、save() が呼び出される前に、アプリケーションでオブジェクトに識別子を割り当てることができます。要素が指定されていない場合、これがデフォルトの戦略です。

戦略を定義しない場合、休止状態はデフォルトで「割り当て済み」になります。「割り当てられた」戦略は、休止状態がアプリケーションが独自の 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)」が実行されると、休止状態は Oracle に対して次の SQL 呼び出しを行い、ID を取得して Country オブジェクトに設定します。

select Country_Id_Seq.nextVal from dual;

トリガーの問題

行が挿入されたときにトリガーを使用してIDをインクリメントしているため、休止状態シーケンスで問題が発生します。Hibernate はシーケンスを使用して ID を生成し、データベースはトリガーを使用して ID を増やします。これにより、ID が 2 回インクリメントされます。

これを解決するには、3 つのオプションがあります。

  1. トリガーは不要なので削除してください。

  2. テーブルがアプリケーションの外部で更新される可能性があるためにまだトリガーが必要な場合は、挿入ステートメントで ID が設定されていない場合にのみ ID が生成されるようにトリガーを更新できます

  3. データをデータベースに保存する前に、トリガーを使用してデータに ID を設定するカスタム 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>

その後、保存後に使用可能になります。

データベース レイヤーでエンティティに加えられた変更は、オブジェクトを更新するまで Hibernate エンティティ レイヤーに反映されません。

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 に答える