サービス クラス内には、メソッドから呼び出される@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 を持つリンク テーブルであることに注意してください。@Embeddable
FOO_ID
FOO
STATE_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; }
}
カスケードを削除したところ、機能しました。どうしてか分かりません。それを説明できる人は誰でも、私から「正解」マークを取得します。:)