[Glassfish EJB] OneToMany geht nicht

damike

Mitglied
Hi

Ich hab Glassfish v3 final und hab folgendes Problem mit EclipseLink:

Java:
@Entity
public class Person implements Serializable
{
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	private String name;
	
	@OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
	private List<Hobby> hobbys;

             // getter und setter
}

Java:
@Entity
public class Hobby
{
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;	
	
	private String name;
	
	@ManyToOne
	@JoinColumn
	private Person person;

              // getter und setter
}

Java:
@javax.ejb.Remote
public interface Testing
{
	public void addTestData();
	public List<Person> getTestData();
}

Java:
@javax.ejb.Stateless
public class TestingBean implements Testing
{
	@javax.persistence.PersistenceContext
	private EntityManager entityManager;
	
	public void addTestData()
	{
		Person p = new Person();
		p.setName("KARL");
		entityManager.persist(p);
		
		Hobby h1 = new Hobby();
		h1.setName("h1");
		h1.setPerson(p);
		
		entityManager.persist(h1);				
	}
	
	public List<Person> getTestData()
	{
		TypedQuery<Person> gridQuery = entityManager.createQuery("SELECT e FROM Person e", Person.class);
		return gridQuery.getResultList();
	}
}

Das Problem ist jetzt wenn ich mit getTestData am Client die Daten abfrage bekomm ich zwar "KARL" - aber die Hobbies nicht ... Das Eager Load greift nicht ...
Ich hab dann mit JOIN FETCH probiert - geht - aber ich bekomme die Ergebnisse durch das JOIN doppelt :S Verwende MySQL - die Daten werden in die Datenbank korrekt geschrieben ...

Weiß jemand was da falsch rennt?

Danke
LG
 
Zuletzt bearbeitet:
Du hast hier eine bidirektionale Relation definiert (Person kennt ihre Hobbies, Hobby kennt seine Person). Bei JPA bist Du selbst dafür verantwortlich, dass beide Enden korrekt sind. Adde beim Code für das Hobby noch folgende Zeile:
Java:
Hobby h1 = new Hobby();
h1.setName("h1");
h1.setPerson(p);
// Die Zeile hier ist neu
person.getHobbies.add(h1);
Da es recht nervig sein kann, daran immer zu denken, macht es Sinn, das Update der Enden in einer der Entities abzufrühstücken. Beispielsweise könnest Du einen Konstruktor für Hobby definieren, in dem Du die Person übergibst und im Konstruktor machst du dann das add.

Auch das getHobbies.add ist nicht aus zwei Gründen nicht so schön:
- Sieht einfach doof aus 😉
- Der getter gibt eine modifiable List zurück
Da wäre es gut, in Person eine public Methode addHobby anzubieten, den normalen getter getHobbies package private zu machen und einen public getter getUnmodifiableHobbies zu implementieren, der mit Collections.unmodifiableList eine unmodifiable List zurück gibt.

[EDIT]
Ups, Dein getter dürfte getHobbys heißen. Im Englischen ist die Mehrzahl korrekterweise hobbies, deswegen hab ich's falsch gemacht.
[/EDIT]
 
Zuletzt bearbeitet von einem Moderator:
Kriegst du eine Exception beim Zugriff auf die Hobbies oder ist die Liste leer?

Duplicates kannst du versuchen mit
Code:
select DISTINCT p from person p
umgehen oder du verwendest ein Set für die Hobbies. Aber dein Problem scheint wo anders zu liegen.
 
@nillehammer: das einfügen geht Problemlos. Die Daten sind meiner Meinung nach korrekt gesetzt:
Hobby hat eine Spalter PERSON_ID - die wird korrekt gesetzt

@Sanix: leere Liste. Sorry - da ich schon so viel probiert habe hab ich vergessen zu erwähnen dass

Exception Description: An attempt was made to traverse a relationship using indirection that had a null Session. This often occurs when a n entity with an uninstantiated LAZY relationship is serialized and that lazy relationship is traversed after serialization. To avoid this issue, ins tantiate the LAZY relationship prior to serialization.

fliegt. Aber warum LAZY wenn ich EAGER verwende?
 
This often occurs when a n entity with an uninstantiated LAZY relationship is serialized and that lazy relationship is traversed after serialization.
Das hat mich noch auf eine Idee gebracht. Hobby implementiert bei Dir gar nicht Serializable. Einen Versuch wäre es wert...
 
Also ich hab meine @ManyToOne Annotations immer so geschrieben:

Java:
@ManyToOne(fetch=FetchType.EAGER)
@JoinColumn(name="PERSON_ID")  //also der Spaltenname der Personenspalte in der Hobbytabelle
private Person person;

Vielleicht hilfts 🙂
 
Hi,

um die Verknüpfungen musst du dich in deiner Anwendung schon selbst kümmern.
Du sagst zwar
Java:
h1.setPerson(p);
, aber damit kennt das Hobby seine Person, aber die Liste der Hobbies in der Person muss ebenfalls geplegt werden! Also das Hobby manuell da über
Java:
p.getHobbies.add(h1);
einpflegen. JPA aktualisiert die Referenzen nicht bei jedem Zugriff auf das Objekt. Wenn es im Cache ist (was es nach dem persist ist), dann holt es auch genau erstmal dieses Objekt und aktualisiert es nicht mit zusätzlichen Informationen. Wären ja für JPA "sinnlose" selects. Referenzen müssen gegenseitig bekannt gemacht werden.
 

Zurück
Oben