# 1:n Beziehung ralisieren



## markai (9. Jun 2012)

Ich hätte gerne dass die User Entities in meiner Applikation Rezepte anlegen können. Es handelt sich also um eine 1:n-Beziehung. Dazu habe ich folgendes gemacht:


```
@Entity @Table(name="Users")
public class User implements Serializable {
    
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String firstname;
    @Column(nullable=false) @NotBlank @Pattern(regexp="\\D+", message="no numbers allowed")
    private String lastname;
  
    private List<Recipe> recipes = new ArrayList<>();
    @Column(nullable=false) @NotBlank
```


```
@Entity
public class Recipe implements Serializable, Comparable<Recipe> {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @Column(nullable=false) @NotBlank
    private String name;
    @Column(nullable=false) @NotBlank
    @Lob
    private String description;
    @ManyToMany(fetch= FetchType.EAGER) @Valid
    private List<Ingredients> ingredients = new ArrayList<>();
    @ElementCollection @Valid
    private List<Comment> comments = new ArrayList<>();
```

Ich hab jetzt zwar einen RecipeController und einen UserController mit dem ich User und auch Recipes anlegen kann, aber wie funktioniert die Zuordnung? Ich hätte gerne 
	
	
	
	





```
recipes.add(new Recipe)
```
 in meinem UserController gemacht, aber das funktioniert ja nicht.
Ich verwende die derby db und jsf.


----------



## diel2001 (9. Jun 2012)

Vielleicht so ?

```
@ManyToOne
private List<Recipe> recipes = new ArrayList<>();
```


----------



## markai (10. Jun 2012)

Sry hab versehentlich die Annotation beim kopieren rausgelöscht. Hatte 


```
@OneToMany
    private List<Recipe> recipes = new ArrayList<>();
```

(Ein User soll ja mehrere Rezepte anlegen können.) Hab dann zwar eine Zwischentabelle "USER_RECIPE" mit den Attributen "USER_ID" und "RECIPE_ID" aber keinen Plan wie ich die Zuordnen soll. Gibt doch sicher eine Möglichkeit dass das JPA das für mich macht?


----------



## cljk (10. Jun 2012)

Du hast eine 1:n und keine n:n Zuordnung. Daher ist eine Zwischentabelle nicht notwendig.

Du wirst User haben (1), die jew. mehrere Rezepte zugeordnet bekommen (n). 

Erweitere deine Recipe-Klasse um das Feld User z.B.

```
@ManyToOne
@JoinColumn(name="CREATED_BY_USER")
User createdByUser;
```
und in deiner User-Klasse beziehst du dich in umgek. Notation auf die Receipe-Klasse

```
@OneToMandy(mappedBy="createdByUser")
List<Receipe> receipesOfUser;
```
Eine Initialisierung mit = new ArrayList... solltest du ggf. sein lassen, außer du brauchst das. Beim Laden aus der DB bekommst du eh nur Objekte mit initialisierten (ggf. leeren) Listen zurück.


Gruß

Marcel


----------



## markai (10. Jun 2012)

Ok die Zwischentabelle ist wohl von einem früheren Versuch übrig geblieben. Danke für den Tipp! Zuordnung funktioniert bestens :toll:

Jetzt hätte ich noch gerne dass auf meiner "home" Seite ALLE Rezepte angezeigt werden.

```
@ManagedBean(name = "recipeController")
@SessionScoped
public class RecipeController implements Serializable {
    ...
    public List<Recipe> getAllRecipes() {
        List result = getFacade().getEntityManager().createQuery("select r from Recipe r").getResultList();
        return result;
```
... was bestens funktioniert. Auf dem jeweiligen Benutzerprofil sollen aber nur eigene Rezepte angezeigt werden:


```
...
SessionBean sessionBean = (SessionBean) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("sessionBean");

    public List<Recipe> getMyRecipes() {
        List result = getFacade().getEntityManager().createQuery("select r from Recipe r where USER_ID = "+sessionBean.getCurrentUser().getId()).getResultList();
        return result;
```

was folgende Exception wirft: 


```
javax.el.ELException: /pages/myProfile.xhtml @34,49 value="#{recipeController.myRecipes}": java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager: 
Exception Description: Error compiling the query [select r from Recipe r where USER_ID = 1], line 1, column 29: unknown identification variable [user_id]. The FROM clause of the query does not declare an identification variable [user_id].
	at com.sun.faces.facelets.el.TagValueExpression.getValue(TagValueExpression.java:114)
	at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:194)
	at javax.faces.component.ComponentStateHelper.eval(ComponentStateHelper.java:182)
	at javax.faces.component.UIData.getValue(UIData.java:731)
	at org.primefaces.component.datatable.DataTable.getValue(DataTable.java:773)
	at javax.faces.component.UIData.getDataModel(UIData.java:1798)
	at javax.faces.component.UIData.getRowCount(UIData.java:356)
	at org.primefaces.component.api.UIData.calculatePage(UIData.java:127)
```

(In der sessionBean wird der aktuelle User gespeichert.) Was mache ich falsch?


----------



## cljk (10. Jun 2012)

Also das einfachste(!) da du die Relation definiert hast ist ja, den User zu laden und über das User-Objekt mit getReceipes() (oder wie es heisst) auf die Rezepte zuzugreifen.

```
sessionBean.getCurrentUser().getReceipes()
```

Die Lösung die du da nutzt, geht auch... du solltest aber daran denken, dass du JPA nutzt und nicht SQL. JPA kennt die Column-Names etc nicht. Ich mag grad nicht nachschlagen - aber du schreibst etwas in der Art wie


```
Query q = getFacade().getEntityManager().createQuery("select r from Recipe r where r.createdByUser = :user");
q.setParameter("user", sessionBean.getCurrentUser());
return q.getResultList();
```


Im übrigen ist auch obiges relativ gesehen Mist. Queries gehören in Transaktionen und damit nicht in die Managementbean sondern in die SessionBean. Der EJB-Container bastelt dann automatisch eine Transaktion drum.

Und noch eine Anmerkung.

```
SessionBean sessionBean = (SessionBean) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("sessionBean");
```
kannste dir vermutlich sparen. Ist das eine RequestScope- ManagementBean?
Dann kannste auch einfach schreiben.


```
@ManagedProperty(value="#{sessionBean}")
SessionBean sessionBean
```
Die sollte dann automatisch injiziert werden - vorausgesetzt der Name stimmt.


----------



## markai (10. Jun 2012)

Ist ja direkt peinlich dass ich nicht selbst dran gedacht hab... Jedenfalls funktioniert es so.

Die sessionBean verwende ich um mein User-Objekt zu speichern wenn dieser sich anmeldet. 

```
@SessionScoped
@ManagedBean(name="sessionBean")
public class SessionBean implements Serializable {
    
    private User currentUser;

// getter u setter
    }
```
Ist zwar vermutlich nicht schön, aber es war die einzige Lösung die mir eingefallen ist... Du meinst also dass Tranksaktionen auch in meine sessionBean rein gehören? Wäre für Verbesserungsvorschläge dankbar.


----------



## cljk (10. Jun 2012)

Nene... jetzt hauen wir gerade zwei Sachen durcheinander (JSF-SessionBean und Stateless Session Bean).

Ich bin davon ausgegangen, dass "getFacade()" dir einen StatelessSessionBean (EJB) zurück gibt. Ist das so?
Wenn ja - gehört die JPA-Anfrage als Methode definitiv da rein und von "getEntityManager" hast du dann auf JSF-Seite die Finger zu lassen. Der EntityManager wäre dann nur gültig für den Kontext der EJB und soll nicht weitergegeben werden - auch wenn es aktuell funktioniert. 
Wenn du allerdings alles irgendwie in JSF Request- und SessionBeans abwickelst, ist das zwar definitiv nicht schön - kann dann bei dem einfachen Projekt so bleiben.


----------



## markai (10. Jun 2012)

Das ist meine Facade.

```
@Stateless
public class RecipeFacade extends AbstractFacade<Recipe> {
    @PersistenceContext(unitName = "cookItLikeABoss_PU")
    private EntityManager em;

    @Override
    public EntityManager getEntityManager() {
        return em;
    }

    public RecipeFacade() {
        super(Recipe.class);
    }
}
```
Verstehe ich das richtig? Ich soll meine queries da reinschreiben und diese dann vom RecipeController aus aufrufen?


----------



## cljk (10. Jun 2012)

markai hat gesagt.:


> Das ist meine Facade.
> Verstehe ich das richtig? Ich soll meine queries da reinschreiben und diese dann vom RecipeController aus aufrufen?



Absolut - Queries gehören in einem JavaEE-Projekt immer in EJBs.

Der Sinn von StatelessSessionBeans (inbes. bei sog. Fassaden) ist u.a. die Behandlung der Datenspeicherung. Alles andere geht - wie du siehst - auch - ist aber ganz böser Stil.


Ich weiss nicht wo du die Oberklasse AbstractFacade her hast - aber da würde ich getEntityManager auf "protected" statt "public" setzen. Dann siehst du recht schnell, was geändert werden muss - weils nämlich nicht mehr läuft *g. Ich vermute mal einfach, dass AbstractFacade manche Basis-Methoden für den Datenzugriff definieren wie z.B. "findByPrimaryKey" o.ä.  und "getEntityManager" benötigt, um den EntityManager der Subklassen zu beziehen. Das ist aber nicht für die JSF-Klassen gedacht.

Also "getAllRecipes()" gehört *definitiv* in die EJB - wenn du getMyReceipes jetzt nur über den Objektzugriff regelst, kannst das in der ManagedBean lassen - mit der Querie-Variante muss das sonst aber def. auch dahin. 
Den Benutzer wirst du ja vermutlich auch über eine Fassade (und damit einer EJB) suchen.

Auf JSF-Seite rufst die Methoden passend auf und leitest das Ergebnis weiter. Ist 3 Zeilen Mehrarbeit - lohnt sich aber - und schöner strukturiert ist es allemale.


So, ich geh jetzt in die Sonne - schönen Sonntag noch.


----------



## markai (10. Jun 2012)

Toll, wieder was gelernt! Ich danke dir. Wünsche auch noch einen schönen Sonntag


----------

