# DTO, JPA Implementierungen, Exception Handling



## pl4gu33 (1. Dez 2012)

Hey,...

so in den letzten Wochen haben sich ein paar Fragen ergeben, die ich nun gesammelt hier in dem Thread bearbeiten wollte... ich hoffe das is okay  ich kennzeichne die Frage mal immer "fett", dass man sie unterscheiden kann. 

*DTO vs. Persistenz Objekte*

Szenario: Man will eine Liste von Personen in z.b. JSF anzeigen und diese dann auch bearbeiten und ggf. speichern können. (Man brauch aber jeweils nur eine Teilmenge der Daten). Dazwischen ist eine LocalBean / Stateless, welche Transaction Einstellungen besitzt (Required). 

Ausgehend von z.b. so einer Entity- Klasse und man braucht nur alle Vereine, alter, name:


```
@Entity
public class Person {

	@Version
	private Long version;
	private Long id;
	private String name;
	private int alter;
	private String lieblingsFarbe;
	private List<Vereine> vereine;
	private List<Hobby> hobbys;
	private List<Person> freunde;
```

Meine Frage ist, was ist hier die beste Lösung,... 

A. Man gibt das Persistenz Objekt nach oben, lädt nur die Daten die man dafür braucht(lazy load). Bearbeitet das Detached- Objekt oben und gibt es wieder runter und behandelt ggf. Exceptions durch Optimistic locking...

B. Man überdenkt seine Struktur, ob man wirklich die ganzen Sachen in der Person braucht

C. Man erzeugt ein DTO, welches nur die Daten, die man wirklich braucht hält. Problem dabei ist, man muss halt die Version mitschleifen und Objekte hin und her konvertieren. 

D. Eure Vorschläge...


*JPA Annotations*

Ich habe ein wenig gegooglet, wohin die Annotations nun wirklich kommen,... irgendwie sagt die eine Seite so, die andere so... wie immer halt  

Daher wollte ich hier mal fragen, wie ihr das seht... ich glaub von Maki hab ich letzten gelesen "FIELD!!!"^^

- Alles auf einen Blick bei Field- Annotation
- Kein Getter/Setter gespamme bei Field- Annotation
- Im Getter kann was anderes passieren, als nur die Rückgabe des Wertes... was aber auch wiederum als positiv angesehn wurde von manchen

- Der Zugriff "direkt" auf Attribute is böse 
- Probleme bei manchen JPA Implementierungen

Einig waren sich nur alle, dass man nicht mixen sollte 

wie gesagt, das sind so Meinungen, die ich gefunden habe... daher wollte ich mal fragen, wie ihr das macht und warum. 

*Hibernate vs. OpenJPA/EclipseLink etc.*

Ich hatte schon einmal festgestellt, dass Hibernate sich bei ManyToMany anders verhält, als es andere Implementierungen tun. 

Ich hab die Annotations gerade an den Getter/Settern, aber wie gesagt dafür hab ich ja die Diskussion auf gemacht 

Beispiel um den Unterschied zu zeigen,... nicht auf Einzelheiten achten: 

Person: 

```
@Entity
public class Person {

	private Long id;
	private String name;
	private int alter;
	private List<Verein> vereine;
	
	protected Person(){
		
	}
	
	public Person(String name, int alter){
		this.alter = alter;
		this.name = name;
		this.vereine = new ArrayList<Verein>();
	}
	
	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="PERSON_SEQ")
	@SequenceGenerator(name="PERSON_SEQ",sequenceName="PERSON_SEQ",initialValue = 1,allocationSize=1)
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAlter() {
		return alter;
	}
	public void setAlter(int alter) {
		this.alter = alter;
	}
	
	@ManyToMany()
	public List<Verein> getVereine() {
		return vereine;
	}
	public void setVereine(List<Verein> vereine) {
		this.vereine = vereine;
	}
	
	public void addVerein(Verein verein){
		this.vereine.add(verein);
		verein.addPerson(this);
	}
}
```

Verein:

```
@Entity
public class Verein {
	private Long id; 
	private List<Person> mitglieder;
	private String vereinsName;
	
	protected Verein(){
		
	}
	
	public Verein(String vereinsName){
		this.vereinsName = vereinsName;
		this.mitglieder = new ArrayList<Person>();
	}
	
	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="VEREIN_SEQ")
	@SequenceGenerator(name="VEREIN_SEQ",sequenceName="VEREIN_SEQ",initialValue = 1,allocationSize=1)
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	
	@ManyToMany(mappedBy="vereine")
	public List<Person> getMitglieder() {
		return mitglieder;
	}
	public void setMitglieder(List<Person> mitglieder) {
		this.mitglieder = mitglieder;
	}
	public String getVereinsName() {
		return vereinsName;
	}
	public void setVereinsName(String vereinsName) {
		this.vereinsName = vereinsName;
	}

	public void addPerson(Person person) {
		this.mitglieder.add(person);
		
	}
}
```

Main- Methode:

```
public class Starter {
	public static void main(String[] args) {
		EntityManagerFactory factory = Persistence.createEntityManagerFactory("HibernateUnit");
		EntityManager em = factory.createEntityManager();			
		init(em);		
		deleteVerein(em);
	}

	private static void deleteVerein(EntityManager em) {
		em.getTransaction().begin();
			em.remove(em.find(Verein.class, 1L));
		em.getTransaction().commit();
	}
	
	private static void init(EntityManager em) {
		em.getTransaction().begin();
		Person person = new Person("Hans",45);
		Verein verein = new Verein("Verein1");
		Verein verein2 = new Verein("Verein2");		
		
		person.addVerein(verein2);
		person.addVerein(verein);	
		em.persist(verein2);
		em.persist(verein);
		em.persist(person);		
		
		em.getTransaction().commit();
	}
}
```

persistence.XML von Hibernate: 

```
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
                        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="HibernateUnit" >
    <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <class>model.Person</class>
    <class>model.Verein</class>
    <properties>    
      <property name="hibernate.connection.driver_class"     value="org.h2.Driver" />
      <property name="hibernate.connection.url"            value="jdbc:h2:D:\\test\testdb" />
      <property name="hibernate.connection.username"       value="sa" />
      <property name="hibernate.connection.password"       value="" />
  	  <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
  	  <property name="hibernate.hbm2ddl.auto" value="create"/>
    </properties>
  </persistence-unit>
</persistence>
```

Persistence XML von OpenJPA:

```
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
                        http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="OpenJPAUnit" >
    <provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
    <class>model.Person</class>
    <class>model.Verein</class>
    <properties>    
      <property name="openjpa.ConnectionDriverName"     value="org.h2.Driver" />
      <property name="openjpa.ConnectionURL"            value="jdbc:h2:D:\\test\testdb" />
      <property name="openjpa.ConnectionUserName"       value="sa" />
      <property name="openjpa.ConnectionPasswort"       value="" />
  	  <property name="openjpa.jdbc.SynchronizeMappings" value="buildSchema(SchemaAction='drop,add')"/>
    </properties>
  </persistence-unit>
</persistence>
```

Verhalten:

OpenJPA löscht die ManyToMany- Beziehung einfach direkt, obwohl noch Abhängigkeiten in der Beziehungstabelle bestehen. 

Hibernate schmeißt hingegen wenn man nix am CascadeMode ändert eine " org.hibernate.exception.ConstraintViolationException". 

Weiterhin habe ich gelesen, dass bei JPA halt beide Seiten die Owner sind.
ManyToMany (Java EE 5 SDK)

Nun ist die Frage, wie ich das Verhalten bei OpenJPA anpassen kann.

Lösungsvorschläge:

A. Ich schau auf dem Objekt was ich löschen will, ob noch Einträge in der Liste vorhanden sind und schmeiße ggf. Exception selber

B. Ich mach ne SQL Abfrage mit Count und schau ob dort noch Einträge vorhanden sind und schmeiße ne Exception


*Exceptionhandling JSF + EJB *

Da man bei EJB meistens eh EJB NestedExceptions bekommt, ist es in JSF eine gute Lösung einfach alles hochzuschmeißen sich ggf. ein ExceptionTranslater zubauen? Dieser iteriert die Nested Exceptions durch und filtert die "richtigen" Exceptions, verpackt diese und wirft sie an einen Exception Handler der in der Web.xml eingestellt wurde? 

Wenn Nein, wie macht ihr das ? Weil ich finde Exception kreuz und quer im Code zu behandeln is irgendwie auch nicht so toll / übersichtlich^^


----------



## Sym (2. Dez 2012)

Hi,

ich beschreibe einfach mal, wie ich das so mache.

*Zu DTO vs. Persistenz Objekte:*

Wenn man in einer JSF Anwendung auf die DB zugreifen möchte, würde ich jetzt nur noch direkt die Entitäten benutzen. Über geschicktes Lazy-Load wird es hier nicht zu langsam. DTOs haben den Nachteil, dass ein Mapping immer Zeit kostet. Weiter hat man eine zusätzliche Objektschicht.

Dies funktioniert natürlich nur, solange das DB-Schema einigermaßen fachlich ist.

*Zu JPA Annotations:*

es gibt verschiedene Meinungen dazu, die sich auch von Zeit zu Zeit immer einmal ändern. Ich schreibe die Annotationen immer an die Felder selbst, da ich dies übersichtlicher finde.

*Zu OpenJPA:*

dies habe ich selbst noch nicht eingesetzt und kann dazu nichts sagen.

*Zu Exceptionhandling JSF + EJB:*

ich definiere meist eigene Exceptions, die von der UI behandelt werden können. Technische Exceptions werden wenn möglich im EJB-Bereich behandelt oder in eine eigene Exception gekapselt.

In der UI kann ich so auf die entsprechenden (fachlicheren) Exceptions reagieren.

Dadurch wird die EJB-Schicht gekapselt und die UI ist nicht von der Implementierung abhängig.


----------



## maki (2. Dez 2012)

> DTO vs. Persistenz Objekte
> 
> A. Man gibt das Persistenz Objekt nach oben, lädt nur die Daten die man dafür braucht(lazy load). Bearbeitet das Detached- Objekt oben und gibt es wieder runter und behandelt ggf. Exceptions durch Optimistic locking...
> 
> ...


Weiss nicht genau was du mit "Persistenz Objekte" meinst, aber DTOs bedeuten eben auch immer dass man einen DTO Assembler/Konverter braucht.

Ansonsten bietet jedes ORM LazyLoading an, ohne Ausnahme.



> JPA Annotations
> 
> Ich habe ein wenig gegooglet, wohin die Annotations nun wirklich kommen,... irgendwie sagt die eine Seite so, die andere so... wie immer halt
> 
> ...


Prüfe deine Quellen.
Wenn ich lange genug Google, finde ich so einiges unglaubliches 

Es macht IMHO überhaupt keinen Sinn Getter zu Annotieren, beim Persistieren will man ja den internen Objektzustand persistieren, am besten so dass das Objekt nix davon mitbekommt.
Welchen Sinn sollte es haben die Werte die persistiert werden sollen nochmals zu verändern im Getter? Um sie dann beim de-persitieren/laden wieder durch den Setter ändern zu lassen??
Wer behauptet "Der Zugriff direkt auf Attribute is böse" hat IMHO die Grundlagen von ORM Persitierung nicht verstanden, das mag in Ausnahmen der richtige Weg sein, aber die Norm ist es eben nicht. 
Das Annotieren von Gettern bricht die Kapselung, plötzlich ist alles per Getter/Setter öffentlich, dafür gibt es IMHO keine gute Ausrede.

Andere empfehlen die Verwendung der ORM.xml, damit hält man den "Domain" Code weitesgehendst frei von Abhängigkeiten und ORM spezifika.

Nachtrag: Wenn man die orm.xml verwendet muss man sich imer noch für Field oder Proeprty Access entscheiden, wobei dass IMHO eben keine echte Entscheidung ist.

Zu 
Hibernate vs. OpenJPA/EclipseLink etc.
Bist du sicher dass du nicht einfach eine Cascade zuviel hast bzw. dir ein Contraint fehlt?
Mag sein dass sich die IMplementierungen was die default Einstellungen betrifft leicht anders verhalten, aber wenn du das explizit festlegst sollte es IMHO genauso gehen.

Zu 
Exceptionhandling JSF + EJB 
Sowas wie ein Exceptionhandler ist IMHO pflicht, willst ja nicht dass sich Clients plötzlich mit Hibrenate/etc. Exceptoins rumschlagen müssen.

Nachtrag 2:
M:N hat so eingie Probleme, es gibt Wege diese bidirektionalen M:N Beziehungen  umzubauen auf unidirektionale M:N Beziehungen.
Man muss ja nicht immer von überall nach überall traversieren können, ein DAO/Repository mit der passenden Suchmethode reciht meist vollkommen aus um macht das Modell einfacher.


----------



## pl4gu33 (3. Dez 2012)

Danke erstmal für eure Antworten ! 
Wenn übrigens irgendwas der folgenden Sachen völlig falsch ist, könnt ihr mir das gerne sagen, dafür frag ich ja 

Also ich unterteile mal wieder:

*Zu DTO vs. Persistenz Objekte:*
okay dann bin ich da schon mal auf dem richtigen Weg mit LazyLoading. Das Problem ist, dass EJB die Transaktions nach jeder Methode zu macht und man halt die Detached- Objekte hat, d.h. für jedes "Nachladen" müsste man ja das Objekt wieder einbinden und nachladen oder gibt es da noch einen anderen Weg? Weil die anderen Annotations außer Required bringen mich da ja nicht weiter... 

*Zu JPA Annotations:*
okay habe ich gelesen und akzeptiert. Ich glaub das Thema ansich is damit erledigt. Danke für die Erläuterung!

*Zu Exceptionhandling JSF + EJB:*
Gut dann bin ich da auch auf dem richtigen Weg!

*Zu OpenJPA:*
Jetzt wirds lustig 

Also ich bin da nun schon ein paar Tage/Stunden dran. Das Problem ist, viele scheinen das einfach hinzunehmen, dass die Daten gelöscht werden. Wenn man sich mal die SQL- Befehle anschaut :

Hibernate


```
Hibernate: alter table Person_Verein add constraint FKC3407DF1A1BF79C8 foreign key (vereine_id) references Verein
Hibernate: alter table Person_Verein add constraint FKC3407DF1D26FE23C foreign key (mitglieder_id) references Person
```

OpenJPA 

```
2781  OpenJPAUnit  TRACE  [main] openjpa.jdbc.SQL - <t 25374911, conn 6829563> executing stmnt 29945686 CREATE INDEX I_PRSNVRN_ELEMENT ON Person_Verein (VEREINE_ID)
2781  OpenJPAUnit  TRACE  [main] openjpa.jdbc.SQL - <t 25374911, conn 6829563> [0 ms] spent
2781  OpenJPAUnit  TRACE  [main] openjpa.jdbc.SQL - <t 25374911, conn 6829563> executing stmnt 27988400 CREATE INDEX I_PRSNVRN_MITGLIEDER_ID ON Person_Verein (MITGLIEDER_ID)
```

Dann hab ich das hier gefunden:
Apache OpenJPA -- FAQ

darauf hin ändert sich der SQL zu:

```
1344  OpenJPAUnit  TRACE  [main] openjpa.jdbc.SQL - <t 7263010, conn 7960257> executing stmnt 22358902 ALTER TABLE Person_Verein ADD FOREIGN KEY (MITGLIEDER_ID) REFERENCES Person (id)
1359  OpenJPAUnit  TRACE  [main] openjpa.jdbc.SQL - <t 7263010, conn 7960257> [15 ms] spent
1359  OpenJPAUnit  TRACE  [main] openjpa.jdbc.SQL - <t 7263010, conn 7960257> executing stmnt 3157607 ALTER TABLE Person_Verein ADD FOREIGN KEY (VEREINE_ID) REFERENCES Verein (id)
```

das ändert aber auch nix weil da irgendwie der constraint fehlt. Leider finde ich auch nicht mehr viel dazu. Ich hab mich mit annotations und JoinTables auseinander gesetzt aber ich bekomme einfach nicht das gewünschte Verhalten hin.

Als Notlösung hab ich mir schon eine eigene Mappingklasse gebaut und die beiden Klassen darin mit ManyToOne / OneToMany verknüpft dort funktioniert es übrigens einwandfrei! 

Aber ich wills mit ManyToMany hinbekommen 

Weiterhin hab ich mich jetzt noch weiter mit ManyToMany + LazyLoading + Mergen beschäftigt dazu hätte ich dann auch noch ne Frage, aber dazu würde ich euch gern ein Codebeispiel tippern, sodass man die Lösungsansätze besser nachvollziehen kann ...das kommt wohl morgen


----------



## pL4Gu333 (4. Dez 2012)

also um das Problem mit OpenJPA nochmal genauer zu erklären... es ist so, dass wenn man direkt in der DB (H2) versucht ein Datensatz mit einer Referenzierung zu löschen klappt das nicht mehr, wenn man 


```
<property name="openjpa.jdbc.MappingDefaults" value="ForeignKeyDeleteAction=restrict, JoinForeignKeyDeleteAction=restrict"/>
```

eingefügt hat. Leider ist es so, dass der REMOVE von OpenJPA zuerst in die Beziehungstabelle geht und dort dann einfach die Beziehung löscht und dann erst den Datensatz in der eigentlichen Tabelle löscht.


----------



## maki (4. Dez 2012)

> okay dann bin ich da schon mal auf dem richtigen Weg mit LazyLoading. Das Problem ist, dass EJB die Transaktions nach jeder Methode zu macht und man halt die Detached- Objekte hat, d.h. für jedes "Nachladen" müsste man ja das Objekt wieder einbinden und nachladen oder gibt es da noch einen anderen Weg? Weil die anderen Annotations außer Required bringen mich da ja nicht weiter...


Fetch Joins z.B.



> Jetzt wirds lustig


Nö, nicht wirklich.

Must dich nur vom Gedanken trennen dass dein ORM dir ein Prod. fertiges Schema erzeugt 
Manuelle Nacharbeit ist da Pflicht.


----------



## pl4gu33 (5. Dez 2012)

okay danke für eure Hilfen/Anregungen... werde mich damit erstmal beschäftigen hab ja nun ein paar weitere Ansätze melde mich bei Fragen wieder danke


----------

