JPA Criteria Query Join von 2 Tables


#1

Ich habe folgende Entities:


@Entity
public class Project implements Serializable {

    @Id
    @GeneratedValue
    private long id;


    public long getId() {
        return id;
    }
        
}


@Entity
public class ProjectLeading implements Serializable {

    @EmbeddedId
    private ProjectLeadingId id;

    public ProjectLeadingId getId() {
        return id;
    }

    public void setId(ProjectLeadingId id) {
        this.id = id;
    }


}


@Embeddable
public class ProjectLeadingId implements Serializable {

    private String userId;
    private long projectId;

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public long getProjectId() {
        return projectId;
    }

    public void setProjectId(long projectId) {
        this.projectId = projectId;
    }

}

Ich möchte mit JPA Criteria Query eine Abfrage schreiben, die mir alle Projekte liefert, mit denen ein bestimmter Benutzer in Verbindung zu ProjectLeading steht.

Ich google mich seit fast einer Stunde weg. Hat jemand einen Rat für mich?

Mein Anfang:


final CriteriaBuilder cb = entityManager.getCriteriaBuilder();
final CriteriaQuery<ProjectLeading> cq = cb.createQuery(ProjectLeading.class);
final Root<ProjectLeading> r = cq.from(ProjectLeading.class);

in normalem sql würde ich sowas schreiben wie


select * from project a join projectleading b on (a.id=b.projectId) where b.userId=2


#2

[B]Querying Relationships Using Joins[/B]

For queries that navigate to related entity classes, the query must define a join to the related entity by calling one of the From.join methods on the query root object or another join object. The join methods are similar to the JOIN keyword in JPQL.

The target of the join uses the Metamodel class of type [INLINE]EntityType[/INLINE] to specify the persistent field or property of the joined entity.

The join methods return an object of type [INLINE]Join<X, Y>[/INLINE], where [INLINE]X[/INLINE] is the source entity and [inline]Y[/inline] is the target of the join. In the following code snippet, [INLINE]Pet [/INLINE]is the source entity, [INLINE]Owner [/INLINE]is the target, and [INLINE]Pet_ [/INLINE]is a statically generated metamodel class:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Pet, Owner> owner = pet.join(Pet_.owners);

Joins can be chained together to navigate to related entities of the target entity without having to create a [INLINE]Join<X, Y>[/INLINE] instance for each join:

CriteriaQuery<Pet> cq = cb.createQuery(Pet.class);

Root<Pet> pet = cq.from(Pet.class);
Join<Owner, Address> address = cq.join(Pet_.owners).join(Owner_.addresses);

http://docs.oracle.com/javaee/6/tutorial/doc/gjivm.html#gjiuv


#3

auf ProjectLeadingId innerhalb von ProjectLeading trifft das womöglich zu, wenn es auch in SQL kaum ein Join sein wird,
allerdings ist Project dann keine Entity sondern nur als Long verknüpft…

für alles muss man entsprechende Fälle finden wenn nicht durch ausprobieren zu erreichen,
evtl. Modell solange mehr an den einfachen Beispielen halten


#4

Landei,

das Dokument habe ich bereits gesehen. Probiere mal mit meinem Beispiel das JOIN auf meinem Modell aus. Du wirst feststellen, dass das so nicht geht, weil owners eine Liste von Entites ist, die bei Projekt oder ProjectLeading nicht existiert. Es muss ein JOIN über die Primärschlüssel stattfinden. Dafür habe ich bisher kein Beispiel gesehen.

SlaterB,

Mein Ziel ist zwischen Projekt und User eine n:m Relation herzustellen, die smybolisiert welche User welche Projekte “leaden”. Am besten ich stelle über ein intelligentes Modell sicher, dass ein User nicht das gleiche Projekt mehrfach leaden kann.

Wie würde ein solches Modell aussehen?


#5

‘ein User nicht das gleiche Projekt mehrfach leaden’ verstehe ich nicht recht, also inhaltlich schon, aber nicht am Modell was gerade was ist, wie die mehrfach-Situation aussähe,

aber ein naheliegender reich technischer Aspekt ist wie gesagt statt ‘long projectId’ direkt ‘Project project’ gemappt,
dann funktionieren die Mechanismen besser,

zudem ProjectLeadingId leider erstmal weg, alles direkt in ProjectLeading rein, dann ist die einfache Normalsituation hergestellt,
bei der ich glauben kann dass ‘Querying Relationships Using Joins’ oder ähnlicher Standard funktioniert

allgemein ist natürlich nicht wünschenswert, solche extra Id-Klassen zu verbieten,
besser wäre wenn sich eine Criteria dazu finden ließe, von mit aber leider nicht


#6

Eine n:m Beziehung kenne ich aus der reinen relationalen Welt ohne ORM als ein 1:n zu einer Zwischentabelle. In diesem Fall ProjectLeading. Ich habe JPA komplett falsch verstanden. Ich bin noch verseucht vom Denken aus der “alten” Zeit, in der ORM nicht existierte. Ich muss das Denken neu lernen. Ich habe das Modell nun so erstellt:


public class User implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "projectLeader")
    private List<ProjectLeading> projectLeadings;

}


@Entity
public class ProjectLeading implements Serializable {

    @Id
    @GeneratedValue
    private long id;
    @NotNull
    @ManyToOne
    private User projectLeader;
    @NotNull
    @ManyToOne
    private Project project;
}


@Entity
public class Project implements Serializable {

    @Id
    @GeneratedValue
    private long id;
    @OneToMany(mappedBy = "project")
    private List<ProjectLeading> projectLeadings;

}

Nun fragen sich welche, warum nicht gleich @ManyToMany.

Das liegt einfach daran, dass mehrere ManyToMany mit dem gleichen Zieltypen nicht von JPA richtig gemappt werden. JPA scheint wohl dafür zu blöd zu sein. Schade wa, aber wenn ich einen Fehler gemacht habe, und es geht doch bitte ich um einen Hinweis.


#7

Also wenn ich das richtig verstehe, willst du ne N:M Beziehung zwischen Project und User?
Hier mal ein Beispiel mit Adresse und Person mit ner Bi-Directionalen ManyToMany Beziehung:


	public static EntityManager em;

	public static void main(String[] args) {
		EntityManagerFactory factory = Persistence
				.createEntityManagerFactory("openJpa");
		em = factory.createEntityManager();
		try {
			Person person = new Person("Hans");
			Adresse adresse = new Adresse("adresse");

			em.getTransaction().begin();
			em.persist(adresse);
			em.persist(person);
			adresse.addPerson(person);
			em.getTransaction().commit();			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		
		
	}
}```

Adresse:
```@Entity
public class Adresse {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long id;
	
	@ManyToMany(mappedBy="adressen")
	private List<Person> persons;
	
	private String name;
	
	
	public Adresse(String  name) {
		this.persons = new ArrayList<Person>();
		this.name = name;
	}
	
	public void addPerson(Person person){
		persons.add(person);
		person.addAdresse(this);

	}
	
	public Long getId() {
		return id;
	}


	public List<Person> getPersons() {
		return persons;
	}
}```

```@Entity
public class Person {
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long id;
	
	@Column(unique=true)
	private String name;
	
	@ManyToMany
	private List<Adresse> adressen;
	
	
	public Person(String name){
		this.name = name;
		this.adressen = new ArrayList<Adresse>();
	}
	
	
	public void addAdresse(Adresse adr){
		adressen.add(adr);
	}
	
	public long getId() {
		return id;
	}
	
}```




org.apache.openjpa.persistence.PersistenceProviderImpl
entitiy.Person
entitiy.Ort
entitiy.Adresse











Es werden automatisch die Tabellen Adresse, Person und Person_Adresse erstellt, wo ein Eintrag vorgenommen wird.