5

サービス クラス内には、メソッドから呼び出される@Transactionalメソッドがあります。このコードが呼び出された時点でアクティブなトランザクションがあることを確認しました。必要なときに DA レイヤーがないことに気付きましたが、現時点では価値があるよりも「正しい」方法で物事を行うのが面倒なレガシー アプリケーションで作業しています。

マッピングは次のようになります。

public class Foo {

  private String id;
  private Bar bar;

  @Id
  @Column(name = "FOO_ID", unique = true, nullable = false, length = 16)
  @GeneratedValue(generator = "blahIdSeq")
  @GenericGenerator(name = "blahIdSeq",
                    strategy = "org.blah.CustomIdGenerator")
  public String getId() {return id;}

  @JoinColumn(name = "FOO_ID")
  @OneToOne(fetch = FetchType.LAZY, optional = false)
  public Bar getBar() { return bar; }

  // SETTERS INCLUDED

}

public class Bar {
  private String id;
  private Foo foo;

  @Id
  @Column(name = "FOO_ID")
  @GeneratedValue(generator = "someSeq")
  @GenericGenerator(name = "someSeq",
                    strategy = "foreign",
                    parameters = {
                      @Parameter(name = "property", value = "foo")
                    })
  public String getId() { return id; }

  @OneToOne(fetch = FetchType.LAZY)
  @PrimaryKeyJoinColumn(name = "FOO_ID")
  public Foo getFoo() { return foo; }

  // SETTERS INCLUDED

}

メソッドは次のようになります。

public String createFoo(Foo foo) {
  Session ses = getSessionFactory().getCurrentSession();
  Bar bar = new Bar();
  bar.setFoo(foo);
  foo.setBar(bar);
  ses.save(foo);
  ses.save(bar);

  System.out.println(foo.getId()); // yields the ID value set by generator
  System.out.println(bar.getId()); // yields same ID value as above

  ses.flush();
  ses.refresh(foo);
}

org.hibernate.SQLロギングを に設定するとDEBUG、Foo と Bar の両方の挿入ステートメントが作成されていることがわかりますが、フラッシュが呼び出された後の更新で例外がスローされますorg.hibernate.UnresolvableObjectException: No row with the given identifier exists

何が原因でしょうか? 使用するデータベースは Oracle 11gR2 です。

アップデート

問題をセッションに絞り込みました。を呼び出すとcurrentSession.flush()、リフレッシュのために期待どおりにデータがデータベースに書き込まれていないようです。メソッドの残りをコメントアウトすると、最後にコミットされ、すべてがデータベースに保存されます。

ただし、フラッシュ/リフレッシュを実行してもハイドレートされたオブジェクトは返されないため、後でトランザクションでデータベースに入力された値 (列のデフォルトで設定) を使用することはできません。メソッドの任意の時点でロールバックできるようにする必要があるため、トランザクションを複数に分割することもできません。

フラッシュがデータベース内のアクセス可能なデータを提供しない理由についてのアイデアはありますか?

別の更新

問題を切り分けようとして多くのコードを移動しましたが、まだ問題があります。また、問題が解決するかどうかを確認するために、すべてを手動で処理するために、2 つのエンティティ間の関係を取り除きました。スティーブからのすべてのコメントを考慮して、ここに私が今持っているものがあります:

public class Foo {

  private String id;
  private Bar bar;

  @Id
  @Column(name = "FOO_ID", unique = true, nullable = false, length = 16)
  @GeneratedValue(generator = "blahIdSeq")
  @GenericGenerator(name = "blahIdSeq",
                    strategy = "org.blah.CustomIdGenerator")
  public String getId() {return id;}

  // SETTERS INCLUDED

}

public class Bar {
  private String id;
  private Foo foo;

  @Id
  @Column(name = "FOO_ID")
  public String getId() { return id; }

  // SETTERS INCLUDED

}

@Service('fooService')
@Transactional(readOnly = true)
class FooService {
  @Autowired
  SessionFactory sessionFactory // populated using Spring config:
                                // org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean

  @Transactional(readOnly = false)
  public void doSomeStuff(Foo fooToSave) {
    sessionFactory.getCurrentSession().saveOrUpdate(fooToSave);
    Bar bar = new Bar(fooToSave); // this populates the Bar.Id field
    sessionFactory.getCurrentSession().saveOrUpdate(bar);
    sessionFactory.getCurrentSession().flush();
    sessionFactory.getCurrentSession().refresh(fooToSave); // exception thrown here
  }
}

さらに別の更新

SQLが同じセッションなどで実行されていることを確認するためにOracleランドでかなり遊んだ後、私は問題を発見しました。Hibernate は SQL バインド変数が設定されていることをログに記録していますが、実際には設定されていません。Oracle 11gR2V$SQL_BIND_CAPTUREを使用して、最後に実行された (insert ステートメントであることが確認された) SQL ID を使用すると、24 個のバインド変数があり、それらのいずれにも値がバインドされていないことがわかりました。値が空白になる原因はまだわかりませんが、答えを見つけるのにかなり近づいています. それは私のマッピングに問題があるに違いありません。

バインド変数であるため、Oracle は挿入できないことを問題視していないと思います。JDBC は通常、検証のためにステートメントに挿入された行数を返すだけINSERTですが、Hibernate の抽象化がこの処理をどのように処理するかは正確にはわかりません。現在、Hibernate 3.6.10 を使用しています。3.6.5 からアップグレードして、問題が解決するかどうかを確認してください。そうではありませんでした。:P

私は誤解されました

上記の「まだ別の更新」セクションは無視してください。V$SQL_BIND_CAPTUREバインド変数は、トランザクションがコミットされるまでビューに表示されないようです。ふりだしに戻る。

別の改訂 - 私は禁止されるつもりだと誓います

基本に立ち返ることにしました。動作状態になってから何が変わったのですか? 主にマッピング。いくつかのサービス レイヤー アイテムも変更されましたが、ほとんどの場合、Hibernate マッピングが XML から注釈に移動されました。Fooそこで、これまで遊んできたのと同じサービス メソッドを使用し、他のすべてのものをコメント アウトして、別の永続オブジェクト タイプを使用して、私がしようとしているのとまったく同じことを試みました。何だと思う?それはうまくいきます。この時点で心痛を引き起こしている可能性のある唯一のリンクは、私が持っている のマッピングですFoo。雇用主が SO に完全なソースを投入することを望んでいるとは思えないので、おそらく自分でこれを理解する必要があります。最終的にそれを理解したら、ある程度の容量で答えを投稿します。

成功!しかし、理由はわかりません...

これが私に問題を与えていたコードです。は、テーブル内の行の参照と別のテーブルの参照で構成される (この例では単に「キー」と呼ばれる) で構成されるBAZ複合 ID を持つリンク テーブルであることに注意してください。@EmbeddableFOO_IDFOOSTATE_ID

public class Foo {

  // OTHER FIELDS INCLUDING IDs AND SUCH

  private Baz bazOfDoom;
  private Baz bazOfLight;
  private Set<Baz> allTheBaz;

  @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
  @JoinColumns({
    @JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID", insertable = false, updatable = false, nullable = false)
    @JoinColumn(name = "DOOM_ID", referencedColumnName = "STATE_ID", insertable = false, updatable = false, nullable = false)
  })
  public Baz getBazOfDoom() { return bazOfDoom; }

  @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
  @JoinColumns({
    @JoinColumn(name = "FOO_ID", referencedColumnName = "FOO_ID", insertable = false, updatable = false, nullable = false)
    @JoinColumn(name = "LIGHT_ID", referencedColumnName = "STATE_ID", insertable = false, updatable = false, nullable = false)
  })
  public Baz getBazOfLight() { return bazOfLight; }

  @OneToMany(mappedBy = "key.foo", fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
  public Set<Baz> getAllTheBaz() { return allTheBaz; }
}

カスケードを削除したところ、機能しました。どうしてか分かりません。それを説明できる人は誰でも、私から「正解」マークを取得します。:)

4

1 に答える 1

0

データベースに保存した後、オブジェクトはオブジェクトの識別子を所有していないようです。そのため、refresh() を呼び出すときに例外が発生します。

実際、データベース テーブルが主キーとして定義されていると仮定しますauto-increment。そのため、最初の Foo オブジェクトを保存すると、主キー列は次のように評価されます1

ただし、save() メソッドを呼び出した後、Hibernate はこの新しく生成された識別子を認識する必要があります。

これを行う最善の方法は、オブジェクトがデータベースに保存されるとすぐに、Hibernate が適切な識別子に再影響することを期待することです。

したがって、オブジェクトがデータベースに保存されるときに識別子を自動的に提供するために、エンティティ クラス内で次の行を見逃す可能性があります。

@GeneratedValue(strategy = GenerationType.AUTO)

もちろん、それらを自動生成する必要はなく、手動で正確にすることができます。

于 2012-09-26T23:16:56.920 に答える