- PR -

EJB3.0で、関連先がインスタンス化されない

投稿者投稿内容
hekigyoku
会議室デビュー日: 2008/12/16
投稿数: 17
投稿日時: 2008-12-16 17:20
EJB3.0の動作がよく分からないので質問させてください。

Person--->Group

という2つのEntityがあります。
Queryを使ってPersonを検索すると、正しく見つかりますが、
Personの属性groupが常にnullです。

一旦Groupの全検索を行い、その後でPersonを検索すると属性groupに値が入っています。

つまり、関連をたどってEntityのインスタンス化をしてくれないのです。
サンプルなどを見ると、きちんとインスタンス化してくれるようだし、

@JoinColumn(name="GROUP_ID0", referencedColumnName="GROUP_ID",nullable=false)

のreferencedColumnNameを変更するとエラーが返るので、Groupの検索SQLは実行されているようなのです・・・
( SELECT GROUP_ID,GROUP_NAME FROM GROUPS WHERE (GROUP_ID0=?) のようなsqlがエラーに吐かれます)

しかし、Groupはnullです。
考えられる理由、調査方法などあれば教えてください。

環境は、
NetBeans6.5
GlassFish-v2
TopLink-essentials
Oracle10g

です。
だっちょ
大ベテラン
会議室デビュー日: 2006/12/05
投稿数: 115
投稿日時: 2008-12-17 09:39
たとえば
Person {
@OneToOne(mappedBy="group",fetch=FetchType.LAZY,name=..
@JoinColumn...
Group getGroup()
}
Group {
@OneToOne
@JoinColumn(..
Group getPerson()
}
としているとか?
 やったことないですが、JoinColumnは
mappedByされるほうだけの1つですむので
もう1つのColumnは設定されないとかあるかも。
hekigyoku
会議室デビュー日: 2008/12/16
投稿数: 17
投稿日時: 2008-12-17 10:33
<Personの一部>
@Entity
@Table(name="USERS")
@NamedQuery(name="findByLastname" ,query="select e from Person e where e.lastname = :lastname")
public class Person implements Serializable{
@Id
@Column(name="USER_ID")
private String id;
@Column(name="LAST_NAME")
private String lastname;
@Column(name="FIRST_NAME")
private String firstname;
@Column(name="MAIL_ADDRESS")
private String mail;
@ManyToOne
@JoinColumn(name="GROUP_ID0", referencedColumnName="GROUP_ID",nullable=false)
private Group group;


<Groupの一部>
@Entity
@Table(name="GROUPS")
public class Group implements Serializable{
@Id
@Column(name="GROUP_ID")
private String id;
@Column(name="GROUP_NAME")
private String name;

こんな感じです。
少し調べたのですが、EAGERフェッチされないのが原因ぽいです。なぜでしょうか。
だっちょ
大ベテラン
会議室デビュー日: 2006/12/05
投稿数: 115
投稿日時: 2008-12-17 12:01
書き方が間違っていたのかはすぐにわかりませんが、単に@OneToManyを入れてないだけでは?

@Entity
@Table(name="USERS")
public class Person implements Serializable{
@Id
@Column(name="USER_ID")
private String id;
..
[明らかな間違いを修正]
@ManyToOne(fetch=FetchType.LAZY, optional=false)
@JoinColumn(name="GROUP_ID0", nullable=false)
private Group group;
// ↑publicにしなくてよかったんでしたっけ?
..

@Entity
@Table(name="GROUPS")
public class Group implements Serializable{
@Id
@Column(name="GROUP_ID")
private String id;
@Column(name="GROUP_NAME")
private String name;
@OneToMany(mappedBy="group")
private Collection<Person> persons;
..



[ メッセージ編集済み 編集者: だっちょ 編集日時 2008-12-17 15:46 ]
hekigyoku
会議室デビュー日: 2008/12/16
投稿数: 17
投稿日時: 2008-12-17 16:17
<Person>
@Entity
@Table(name="USERS")
@NamedQuery(name="findByLastname" ,query="select e from Person e where e.lastname = :lastname")
public class Person implements Serializable{
@Id
@Column(name="USER_ID")
private String id;
@Column(name="LAST_NAME")
private String lastname;
@Column(name="FIRST_NAME")
private String firstname;
@Column(name="MAIL_ADDRESS")
private String mail;
@ManyToOne(fetch=FetchType.EAGER, optional=false)
@JoinColumn(name="GROUP_ID0", referencedColumnName="GROUP_ID",nullable=false)
private Group group;

<Group>
@Entity
@Table(name="GROUPS")
public class Group implements Serializable{
@Id
@Column(name="GROUP_ID")
private String id;
@Column(name="GROUP_NAME")
private String name;
@OneToMany(mappedBy="group")
private Collection<Person> persons = new LinkedList<Person>();

試しにこのように変えてみましたが、やはりGroupはnullのままでした。
SessionBeanの検索メソッドの中で、

for(Person p : persons) p.getGroup();

とやると、groupが入る事は確認できましたので、やはりgroupのフェッチをする前に
EntityがWEB層に出て行ってしまっていることが原因のようです。

EAGERだと、Personオブジェクトを作ったときに同時にGroupもフェッチしてくれるのではないのでしょうか・・・
だっちょ
大ベテラン
会議室デビュー日: 2006/12/05
投稿数: 115
投稿日時: 2008-12-17 17:23
>EntityがWEB層に出て行ってしまっていることが原因のようです。

 ひょっとしてEJBのインタフェースでEntityオブジェクトを返していて、
それをWeb層で見るとリレーションがクリアされているという話なのでしょうか?
 そうだとすると、Entityオブジェクトをそのまま返すインタフェースにしているのがまずいと思います。リレーション情報は単純にはインタフェースで復帰させることはできなかったはず。
 そうでなければ下記と同様のコードでセッションBeanでデータを取得する限り問題はなかったと記憶していますので、私はこれ以上力になれそうもありません。
hekigyoku
会議室デビュー日: 2008/12/16
投稿数: 17
投稿日時: 2008-12-17 21:07
>ひょっとしてEJBのインタフェースでEntityオブジェクトを返していて、
>それをWeb層で見るとリレーションがクリアされているという話なのでしょうか?

はい。おっしゃるとおりです。

>そうだとすると、Entityオブジェクトをそのまま返すインタフェースにしているのがまずいと思います。リレーション情報は単純にはインタフェースで復帰させることはできなかったはず。
>そうでなければ下記と同様のコードでセッションBeanでデータを取得する限り問題はなかったと記憶していますので、私はこれ以上力になれそうもありません。

ServletはPersonをダイレクトに参照しています。
確かに、そのままServletにEntityを返すと、EntityManagerの支配下から離れてDetatched?になるようですが、
そもそも最初のquery("findByLastname")を実行したと同時にGroupもフェッチしてくれればdetatchedになっても問題ないと
思うのです。

EAGERフェッチというのは、そういう意味ではないのでしょうか?
LAZYフェッチは、必要なときにフェッチすると言う事なので、必要なときにdetatchedだと検索できないのは理解できます。
しかしEAGERなら・・・
だっちょ
大ベテラン
会議室デビュー日: 2006/12/05
投稿数: 115
投稿日時: 2008-12-18 09:22
 EJBで復帰するObjectはたとえLocalであっても元のObjectではありません。リモートインタフェースに変更できることを考えると別サーバのObjectにそのままアクセスできないとわかると思います。
 基本的なObjectは復元されますが、リレーション先までは復元されないためそのままでは取得できません。仮にリレーション先を復元した場合、極端なことをいれば1つのEntityを取得するのにDB全部を復元するような事態がありえるでしょう。
 なので、リレーション先のデータを取得する場合はEJBのインタフェースを考える必要があります。リレーション先のデータを取得するインタフェースを追加してもいいですが、私の場合、EntityクラスがimplementするインタフェースでIDリストを返すようにしておき、XMLにmarhalしてインタフェースのObjectを復元するようにしてます。復元したEntityのリレーションはそのIDリストからIDで取得するようにしています。

スキルアップ/キャリアアップ(JOB@IT)