2012-09-10 19 views
5

私はインターネット上で多くの投稿をチェックしましたが、私の問題は解決しません。誰かが助けることができれば本当に感謝します! 私はOneToMany親子関係を持っています。子コンポジット・キーの1つが親の外部キー(Level1)である場合、Hibernateは挿入子(Level2)をカスケードしません。私はcascade = "all"を指定し、その逆が真か偽であっても、結果は同じです。例外はなく、挿入するだけではありません。プリントアウトの下にLevel1が正常に挿入されたがLevel2のみが選択され、挿入されていないことが示されています。親に外部キーを持つ子のカスケード挿入はありません

休止:sst.level_1(STATUS、NAME)値を挿入 休止(?):level2x_.LEVEL_1_ID = sst.level_2 level2x_からCATEGORY4_としてlevel2x_.LEVEL_1_ID、level2x_.TIMESEGMENT、level2x_.CATEGORYを選択? level2x_.TIMESEGMENT =?

APIは、Hibernate 4.1.6/Spring 3.1.2です。ここ

は、MySQLのテーブル定義である:ここ

CREATE TABLE level_1 
(
ID int NOT NULL PRIMARY KEY AUTO_INCREMENT, 
STATUS int, 
NAME varchar(255) 
); 

CREATE TABLE level_2 
(
LEVEL_1_ID int, 
TIMESEGMENT int, 
CATEGORY varchar(2), 
PRIMARY KEY (LEVEL_1_ID, TIMESEGMENT), 
FOREIGN KEY (LEVEL_1_ID) REFERENCES level_1(ID) ON DELETE CASCADE 
); 

は、テストコードです。レベル1のため

public class Test { 

    public static void main(String[] args) { 
     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); 
     doInsert(context); 
    } 

    private static void doInsert(ApplicationContext context) { 

     Level1 level1 = new Level1(); 
     Level2 level2 = new Level2(); 

     level1.setName("LEVEL 1 NAME"); 
     level1.setStatus(1); 
     level1.getLevel2s().add(level2); 

     level2.setLevel1(level1); 
     level2.setCategory("CA"); 
     level2.setId(new Level2Id(level1.getId(), 10)); 

     Level1DAO level1DAO = (Level1DAO) context.getBean("Level1DAO"); 
     level1DAO.save(level1); 
    } 
} 

マッピング:Level2のため

<hibernate-mapping> 
    <class name="com.jc.hibernate.Level1" table="level_1" catalog="sst"> 
     <id name="id" type="java.lang.Integer"> 
      <column name="ID" /> 
      <generator class="identity" /> 
     </id> 
     <property name="status" type="java.lang.Integer"> 
      <column name="STATUS" /> 
     </property> 
     <property name="name" type="java.lang.String"> 
      <column name="NAME" /> 
     </property> 
     <set name="level2s" inverse="true" cascade="all"> 
      <key> 
       <column name="LEVEL_1_ID" not-null="true" /> 
      </key> 
      <one-to-many class="com.jc.hibernate.Level2" /> 
     </set> 
    </class> 
</hibernate-mapping> 

マッピング:

<hibernate-mapping> 
    <class name="com.jc.hibernate.Level2" table="level_2" catalog="sst"> 
     <composite-id name="id" class="com.jc.hibernate.Level2Id"> 
      <key-property name="level1Id" type="java.lang.Integer"> 
       <column name="LEVEL_1_ID" /> 
      </key-property> 
      <key-property name="timesegment" type="java.lang.Integer"> 
       <column name="TIMESEGMENT" /> 
      </key-property> 
     </composite-id> 
     <many-to-one name="level1" class="com.jc.hibernate.Level1" update="false" insert="false" fetch="select"> 
      <column name="LEVEL_1_ID" not-null="true" /> 
     </many-to-one> 
     <property name="category" type="java.lang.String"> 
      <column name="CATEGORY" length="2" /> 
     </property> 
    </class> 
</hibernate-mapping> 

答えて

1

あなたのマッピングが正しいように思えます。行この行が実行される

level2.setId(new Level2Id(level1.getId(), 10)); 

あるよう

あなたの問題が見え、LEVEL1はまだそのIDが割り当てられていないので、それはnullになります。 level_1_idとsegmentは複合主キーですが、いずれもnullにはなりません。 ですから、最初

Integer generatedId = (Integer) Session.save(level1) 

を行う必要がありますこれは、データベース内の即時の挿入を行い、識別子を返します。 そして、あなたはLever2Id

level2.setId(new Level2Id(generatedId , 10)); 

を作成するための識別子を使用することができます私はあなたが与えられたマッピングとその作業罰金でのMySQLでこれを試してみました。私が最初にlevel1最初を保存しない場合

は、それが

Column 'LEVEL_1_ID' cannot be null 

残念ながら、それはカスケードのように見える

UPDATEが動作しない私にエラーを与える複合キーがある自動的子の場合複合キーの列のいずれかが親の主キーを参照しています。したがって、上に示したようにIDと関係を手動で設定する必要があります。

Childが単一の列IDを持ち、リレーションが1対1である場合、Hibernateは子のプライマリ列がParentから来ており、自動的に設定できることを知ることができます。herehere

ドキュメントはあまりにもあなたが複合キーを生成するためにIdentifierGeneratorを使用することはできません」このhere

を示唆してご覧ください。 代わりに、アプリケーションは、独自の識別子を割り当てる必要があります。」

複合IDマッピングを表す休止クラスは、(成分のために使用され、複合要素は、複合識別子など)org.hibernate.mapping.Componentとデフォルトであります独自のCompositeUserType(コンポジットIDの場合)を作成し、hereのようなカスタム識別子ジェネレータを使用することで、あなたが邪魔にならないようにするカスタムIDジェネレータを提供していない限り、しかし、これは醜いと過剰な見えます。親と子の両方を節約し、Hibernateが何の推移持続性がないようにまず、あなたの親セット要素からカスケードにすべてのオプションを削除するか、それを「なし」に設定トランザクションで、手動で関係を設定することについて

。親を保存すると、親を保存するだけで、子は保存しません。

次に、使用可能なすべてのプロパティを持つ親と子を準備し、モデルの一貫性を保証するために、このように両側からオブジェクト関係を設定します。

Parent parent = new Parent(); 
    parent.setStatus(1 ); 
    parent.setName("Parent"); 
    ChildId childId = new ChildId();  
    Child child = new Child(childId);  
    child.setCategory("TH");  
    parent.addChild(child); 
    child.getId().setTimesegment(10); 

親のはaddChildメソッドはコレクションに子を追加するだけでなく、セットには、子の親は、その後、パラメータとして子をとり、また、トランザクションであるDAOのメソッドを持っている1つの便利な方法で

public void addChild(Child child){ 
    if (child !=null){ 
     getChildrens().add(child); 
     child.setParent(this); 
    } 

    } 

ですこのような何か(より良い方法ではなく、DAO層のサービス層のトランザクションを作ることですが)今のよう

public void save(Child child){ 

    //get Session 
    Session session =....... 
    Transaction tx = session.beginTransaction(); 
    Integer generatedId = (Integer)session.save(child.getParent()); 
    ChildId childId = child.getId(); 
    childId.setChildId(generatedId);  
    session1.save(child); 
    tx1.commit(); 
    HibernateUtil.closeSession(); 
} 

これは私が提案することができるものです。

+0

返信いただきありがとうございます。レベル1のIDがヌルであることがわかります。これが問題の原因です。この場合、レベル1と2の間でトランザクションをアトミックにするようにトランザクションを構成するにはどうすればよいでしょうか? – John

+0

トランザクションで子と親の両方を保存するための更新を表示 – Shailendra

0

更新するだけです。複合IDを削除しました。代わりに、データベースで生成された代理キーを子テーブルに使用し、外部キーを親IDに使用します。これは、cascade = "all"との1対多の関係で機能します。親を救うことは、子供を1つのステップで保存します。これは、元の質問を解決するのではなく、代替案です。

CREATE TABLE level_11 (
ID int NOT NULL PRIMARY KEY AUTO_INCREMENT, 
STATUS int, NAME varchar(255)); 

CREATE TABLE level_22 (ID int NOT NULL PRIMARY KEY AUTO_INCREMENT, 
LEVEL_11_ID int NOT NULL, 
TIMESEGMENT int NOT NULL, 
FOREIGN KEY (LEVEL_11_ID) REFERENCES level_11(ID) ON DELETE CASCADE, 
CONSTRAINT UQ_LEVEL2 UNIQUE (LEVEL_11_ID, TIMESEGMENT)); 
関連する問題