# Architektur für JPA Hibernate Umstieg



## looparda (23. Okt 2016)

Hallo, ich habe ein OSS Projekt bei dem als ORM ActiveJDBC gewählt wurde. Das Programm parst Rezepte aus verschiedenen Quellen und legt diese in der Datenbank ab.
Die Datenbank wird ihrerseits von einer JavaFX GUI benutzt.
Ein Rezept hat recht viele Aggregationen zu anderen Objekten wie Kategorie, Quelle, Zutaten (welches wiederum Aggregationen von Einheit und Menge hat)

Während der Implementierungsphase traten Probleme mit der "Active Record" Architektur der Library auf. Eins davon:
- Ein Parser versucht eine Quelle zu parsen und baut dazu ein Rezept Objekt zusammen. Möchte man nun die Aggregation eines Kategorie zum Rezept bilden muss die Kategorie zuerst in der Datenbank gespeichert sein. 
Gibt der Parser nun das Rezept an den Aufrufer zurück und dieser entscheidet, dass das Rezept unvollständig ist weil bspw. der Titel des Rezepts fehlt, befinden sich Objekte in der Datenbank die eigentlich noch gar nicht in der Datenbank sein sollten aber man durch den Active-Record Ansatz dazu gezwungen wurde.
Dies löste ich nun durch Trennung von Datenbankobjekten und POJO. D.h. Alle Objekte aus der Datenbank wurden zur Verwendung in der Applikationslogik und GUI erst in POJO gemappt. POJO, die in die Datenbank sollten wurden nun erst zu Datenbankobjekten gemappt. Das ist eine Heidenarbeit, da ich bei POJO, die persistiert werden sollen erst überprüfen musste ob die Aggregationen bereits persistiert sind, ob die Aggregation bisher nur auf Objektebene besteht (falls ja, Beziehung in Datenbank anlegen - natürlich auch hier weider erst das Aggregationsende persistieren, das wiederum Aggregate hat) und weitere Dinge. Das wurde sehr schnell sehr komplex und ich implementierte dort Persistierungsregeln, die mir mir die Library eigentlich abnehmen sollte.

Es hat sich also während der Implementierung herausgestellt, dass die Architektur der Datenbankanbindung unstimmig ist. Ich bin auf der Suche nach der richtigen Architektur und benötige dazu Hilfe. 
Der Active Record Ansatz ist gestorben, wenn man POJO bzw. das DAO Pattern anwenden möchte. Zwangsläufig denke ich also über den Wechsel der Datenbank-Library nach und möchte gern JPA mit Hibernate einsetzen. Allerdings überfordert mich diese Library auch ein wenig, da ich nicht weiß in welche Ebene/Stelle das persistieren gehört - und das möglichst generisch mit GenericDAO Ansatz.

Eigentlich kann mir jeder helfen, der eine Datenbank mit Hibernate angebunden hat und denkt, dass es gut umgesetzt ist. 
Würde mich auch durch andere OSS Projekte wühlen, falls ihr welche empfehlen könnt.


----------



## Tobse (23. Okt 2016)

Zunächst: Ich verstehe das Problem nicht so recht; wenn eine Entität nicht in die DB soll, darf man es eben nicht über den DAO speichern. Es würde ja genügen, die Entitäten des Parsers alle erstmal zu sammeln, zu prüfen und dann erst die brauchbaren dem DAO zu übergeben. Aber ich kenne eure Architektur nicht, ich übersehe warhscheinlich einen wichtigen Aspekt.

Vielleicht gelingt es mir, dir das Konzept von Hibernate schnell zu erklären:

Die Herangehensweise von Hibernate nennt sich Unity of Work. Die Idee ist, dass die Entitäten auch völlig unabhängig von der DB funktionieren. Das erkennen von Änderungen und das persistieren übernimmt Hibernate komplett. Das einzige was man noch manuell tun muss, ist das Framework anzuweisen, welche Entities neu hinzugekommen sind und welche entfernt werden sollen.

Dadurch erreicht man folgendes:

Unit-Testbarkeit der Business-Logik (im Gegensatz zu Intergrationstests mit einem ADO-Ansatz)
man spart sich einen Haufen DB-Spezifischen Code


----------



## Dompteur (23. Okt 2016)

looparda hat gesagt.:


> ...
> - Ein Parser versucht eine Quelle zu parsen und baut dazu ein Rezept Objekt zusammen. Möchte man nun die Aggregation eines Kategorie zum Rezept bilden muss die Kategorie zuerst in der Datenbank gespeichert sein.
> Gibt der Parser nun das Rezept an den Aufrufer zurück und dieser entscheidet, dass das Rezept unvollständig ist weil bspw. der Titel des Rezepts fehlt, befinden sich Objekte in der Datenbank die eigentlich noch gar nicht in der Datenbank sein sollten aber man durch den Active-Record Ansatz dazu gezwungen wurde.


Unterstützt ActiveJDBC keine Transaktionen ? Ich kenne die Library nicht, aber folgender Link http://javalite.io/transactions läßt es mich vermuten.


----------



## looparda (23. Okt 2016)

Tobse hat gesagt.:


> Zunächst: Ich verstehe das Problem nicht so recht; wenn eine Entität nicht in die DB soll, muss ja sie ja auch nicht über das DAO speichern. Es würde ja genügen, die Entitäten des Parsers alle erstmal zu sammeln, dann zu prüfen und die brauchbaren dann dem DAO zu übergeben. Aber ich kenne eure Architektur nicht, ich übersehe warhscheinlich einen wichtigen Aspekt.


Das Active-Record Pattern hat mich dazu gezwungen, da die Aggregate bereits persistiert sein müssen um sie im Objekt zu setzen. Das "sammeln" habe ich dann so gelöst, dass ich ausschließlich mit POJO in Parser/GUI gearbeitet habe und nur zum persistieren/laden nach/von zu einem Active Record database object gemappt habe.
Ich habe angefangen auf Hibernate umzustellen und ein wenig Erfahrung gesammelt. Aber auch hier treten wieder Design Probleme auf, die ich nicht zu bewältigen weiß.



Dompteur hat gesagt.:


> Unterstützt ActiveJDBC keine Transaktionen ? Ich kenne die Library nicht, aber folgender Link http://javalite.io/transactions läßt es mich vermuten.


Guter Punkt! Ja Transaktionen habe ich auch schon benutzt mit der Library. Ich möchte aber allgemein vom Active Record und Active JDBC mit fieser Bytecode-Instrumentierung weg.

Ich suche nach Best-Practices und Projekten in denen man eine Architektur in der Gesamtheit mit mehreren Entities und vollem Lifecycle/CRUD einer Entity finden kann. Ich finde bloß immer Tutorials wo gezeigt wird wie man einen Aspekt von CRUD mit Hibernate löst - super das bekomme ich auch hin aber ich suche nach einer Architektur.
Mein Ansatz war dieser:

```
@SuppressWarnings("unchecked")
public class GenericDAOImpl<E, ID extends Serializable> implements IGenericDAO<E, ID> {

    @Inject
    private EntityManager em;

    protected Class<? extends ID> daoType;

    public GenericDAOImpl() {
        Type t = getClass().getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) t;
        daoType = (Class) pt.getActualTypeArguments()[0];
    }

    @Override
    public void add(E entity) {
        em.getTransaction().begin();
        em.persist(entity);
        em.getTransaction().commit();
    }

    @Override
    public void saveOrUpdate(E entity) {
        em.getTransaction().begin();
        em.merge(entity);
        em.getTransaction().commit();
    }

    @Override
    public void update(E entity) {
        em.getTransaction().begin();
        em.merge(entity);
        em.getTransaction().commit();
    }

    @Override
    public void remove(E entity) {
        em.getTransaction().begin();
        em.remove(entity);
        em.getTransaction().commit();
    }

    @Override
    public E find(ID key) {
        em.getTransaction().begin();
        final E e = (E) em.find(daoType, key);
        em.getTransaction().commit();
        return e;
    }

    @Override
    public List<E> getAll() {
        return em.createQuery("Select t from " + daoType.getSimpleName() + " t").getResultList();
    }

    @Override
    public Optional<E> findFirst(String field, Object value) {
        EasyCriteria<? extends ID> easyCriteria = EasyCriteriaFactory.createQueryCriteria(em, daoType);
        return Optional.ofNullable((E) easyCriteria.andEquals(field,value).getSingleResult() );
    }

    @Override
    public E findOrCreate(String field, Object value) {
        throw new NotImplementedException();
    }

    @Override
    public E findOrCreate(E entity) {
        if ( !em.contains(entity) ) {
            add(entity);
        }
        return entity;
    }
}
```
Womit ich für jede Entity einen DAO anlege:

```
public class RecipeDAOImpl extends GenericDAOImpl<Recipe, Integer> implements IRecipeDAO {
```
Den ich per

```
@Inject
IRecipeDAO recipeDAO
```
benutzen kann.
Aber auch hier habe ich wieder angefangen. Wie hängt alles zusammen / wie löse ich bspw, dass ein Rezept zu einer Kategorie gehört, es aber ausreicht, wenn die Kategorie "wiederverwendet" wird und somit mehrere Rezepte die selbe Kategorie aggregiert haben. Oder hier am Beispiel das gleiche Problem mit Zutaten und deren Einheit und Mengenangabe.

```
public class RecipeDAOImpl extends GenericDAOImpl<Recipe, Integer> implements IRecipeDAO {

    @Inject
    private IIngredientDAO ingredientDAO;
    @Inject
    private IUnitDAO unitDAO;

    @Override
    public void addIngredient(final Recipe recipe, final String ingredientName, final double amount, String unitName) {

        final RecipeIngredient recipeIngredient = new RecipeIngredient();
        final Optional<Ingredient> persistedIngredient = ingredientDAO.findFirst("name", ingredientName);
        final Optional<Unit> persistedUnit = unitDAO.findFirst("name", unitName);

        if( !persistedIngredient.isPresent() ) {
            final Ingredient ingredient = new Ingredient();
            ingredient.setName(ingredientName);
            ingredientDAO.add(ingredient);
            recipeIngredient.setIngredient(ingredient);
        } else {
            recipeIngredient.setIngredient( persistedIngredient.get() );
            persistedIngredient.get().getRecipeIngredients().add(recipeIngredient);
        }

        if( !persistedUnit.isPresent() ) {
            final Unit unit = new Unit();
            unit.setName(unitName);
            unitDAO.add(unit);
            recipeIngredient.setUnit(unit);
        } else {
            recipeIngredient.setUnit( persistedUnit.get() );
            persistedUnit.get().getRecipeIngredients().add(recipeIngredient);
        }

        recipeIngredient.setAmount(amount);
        recipeIngredient.setRecipe(recipe);
        recipe.getRecipeIngredients().add(recipeIngredient);
        recipeIngredient.setRecipe(recipe);
    }
}
```
Das ist doch alles hässlich und verunsichert mich ob man das so aufbaut. Ist das Applikationslogik? Falls ja fühlt es sich ziemlich gefrickelt an so etwas "gewöhnliches" auf dieser Ebene zu tun.


----------



## Tobse (23. Okt 2016)

looparda hat gesagt.:


> Das Active-Record Pattern hat mich dazu gezwungen, da die Aggregate bereits persistiert sein müssen um sie im Objekt zu setzen. Das "sammeln" habe ich dann so gelöst, dass ich ausschließlich mit POJO in Parser/GUI gearbeitet habe und nur zum persistieren/laden nach/von zu einem Active Record database object gemappt habe.
> Ich habe angefangen auf Hibernate umzustellen und ein wenig Erfahrung gesammelt. Aber auch hier treten wieder Design Probleme auf, die ich nicht zu bewältigen weiß.


Hibernate erforder auch die eine oder andere Design-Anpassung, das wird sich nicht vermeiden lassen.
Hibernate löst diese Abhängigkeiten aber selbst auf. Relationen, welche noch nicht in der Datenbank sind, legt Hibernate selbst an. Die Zuordnung kann völlig unabhängig von der DB passieren.



looparda hat gesagt.:


> Ich möchte aber allgemein vom Active Record und Active JDBC mit fieser Bytecode-Instrumentierung weg.


Hibernate ist da kein stückchen besser. Es erstellt Bytecode zur Laufzeit und benutzt Reflection, um Dinge zu Tun, die man eigentlich nicht tun sollte.



looparda hat gesagt.:


> Iich suche nach einer Architektur.


Ganz grob angerissen:

Entity Recipe
interface RecipeRepository: Definiert Abfragen (z.B. find(int id), findAllContainingIngredient(Ingredient ingredient), ...)
class EMRecipeRepository implements RecipeRepository: Kommuniziert mit dem JPA EntityManager, sonst keine andere Klasse
class RecipeService: Entählt die Buisiness-Logik und benutzt ein RecipeRepository für die Persistenz.

Im Unit-Test kannst du dann RecipeRepository mocken.


----------



## Tobse (23. Okt 2016)

Hier mal ein schnell zusammengehacktes Beispiel (nichtmal kompiliert, einfach nur runtergeschrieben):


```
@Entity
public class Recipe {
    private String name;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "ingredient", orphanRemoval = true, fetch = FetchType.EAGER)
    private Set<IngredientAssociation> ingredients;

    private Recipe() { /* JPA */ }

    public Recipe(String name) {
        this.name = name;
    }

    public void setIngredientAmount(Ingredient ingredient, double amount, Amount.Unit amountUnit) {
        if (/* ingredients enthält einen eintrag für ingredient */) {
           eintrag.amount().setValue(amount);
            eintrag.amount().setUnit(amountUnit);
        } else {
            ingredients.add(new RecipeIngredientAssociation(ingredient, amount, amountUnit));
        }
    }

    // u.s.w.
}

interface RecipeRepository {
    public Recipe find(int id);

    public Collection<Recipe> findAllContainingIngredients(Ingredient... ingredients);

    public boolean add(Recipe recipe);

    public boolean remove(Recipe recipe);
}

@Singleton
class RecipeService {
    private RecipeRepository repository;
    private IngredientRepository ingredientRepository;

    @Inject
    public RecipeService(RecipeRepository repository, IngredientRepository ingredientRepository) {
        this.repository = repository;
        this.ingredientRepository = ingredientRepository;
    }

    // Read + Delete methoden
    public void setRecipeIngredientAmount(int recipeId, int ingredientId, double amount, Amount.Unit amountUnit) {
        Recipe r = repository.find(recipeId);
        Objects.requireNonNull(r, "Recipe #" + recipeId + " existiert nicht!");
    
        Ingredient i = ingredientRepository.find(ingredientId);
        Objects.requireNonNull(i, "Ingredient #" + ingredientId + " existiert nicht!");
    
        r.setIngredientAmount(i, amount, amountUnit);
    }
}

@Singleton
class EMRecipeRepository implements RecipeRepository {
    private EntityManager em;

    @Inject
    public EMRecipeRepository(EntityManager em) {
        this.em = em;
    }

    @Override
    public Recipe find(int id) {
        return this.em.find(Recipe.class, id);
    }

    @Override
    public Collection<Recipe> findAllContainingIngredients(Ingredient... ingredients) {
        return this.em.createQuery(
            "SELECT r, ri, i FROM Recipe JOIN r.ingredients ri JOIN ri.ingredient i WHERE i IN (?1)"
        )
            .setParameter(1, ingredients)
            .getResult();
    }

    @Override
    public boolean add(Recipe recipe) {
        if (!this.em.contains(recipe)) {
            this.em.persist(recipe);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public boolean remove(Recipe recipe) {
        if (this.em.contains(recipe)) {
            this.em.remove(recipe);
            return true;
        } else {
            return false;
        }
    }
}

@Entity
class RecipeIngredientAssociation {
    private Ingredient ingredient;
    private Amount amount;

    private RecipeIngredientAssociation() { /* JPA */ }

    public RecipeIngredientAssociation(Ingredient ingredient, double amount, Amount.Unit amountUnit) {
        this.ingredient = ingredient;
        this.amount = new Amount(amount, amountUnit);
    }

    @Transient
    public Ingredient getIngredient() {
       return ingredient;
    }

    @Transient
    public Amount amount() {
       return amount;
    }
}

@Embeddable
public class Amount {
    private double value;
    private Unit unit;

    private Amount() { /* JPA */ }

    public Amount(double value, Unit unit) {
      this.value = value;
      this.unit = unit;
    }

    @Transient
    public double getValue() {
       return this.value;
    }

    @Transient
    public double getUnit() {
        return this.unit;
    }

    @Transient
    public void setValue(double value) {
        this.value = value;
    }

    @Transient
    public void setUnit(Unit unit) {
        this.unit = unit;
    }

    public static enum Unit {
        GRAMMS,
        LITERS,
        OUNCES,
        GALLONS // wahrscheinlich unnötig, aber was solls
    
        // kann auchnoch umrechungsmethden beinhalten
    }
}

@Entity
public class Ingredient {
    @Id
    @GeneratedValue
    private int id;

    private String name;

    private Ingredient() { /* JPA */ }

    public Ingredient(String name) {
        this.name = name;
    }

    @Transient
    public int getId() {
        return this.id;
    }

    @Transient
    public String getName() {
        return this.name;
    }
}
```

Das kann nun verwendet werden:

```
Ingredient salt= new Ingredient("Salz");
Ingredient wasser = new Ingredient("Wasser");
Recipe r = new Recipe("Salzwasser");
r.setIngredientAmount(wasser, 2, Amount.Unit.LITERS);
r.setIngredientAmount(salz, 20, Amount.Unit.GRAMMS);
recipeRepository.add(r);
entityManager.flush(); // Tada, fertig; Rezept, Salz, Wasser und die Zuordnungen sind in der DB
```


----------



## looparda (23. Okt 2016)

Vielen Dank! Das ist im Prinzip so wie ich es schon habe. Das bestärkt mich, dass ich auf dem richtigen Weg bin.

Meine Beispiele für das wiederverwenden von Entities: ich möchte nicht 100x Zucker in der Datenbank haben, nur weil jedes 2. Rezept Zucker beinhaltet. Das habe ich ja versucht zu Umgehen durch meine addIngredient Methode. Allerdings ist das new RecipeIngredient dort noch fehl am Platz, da dies dafür sorgt, dass eine neue Assoziation angelegt wird. (Was aus der Sicht eines Updates von einem Objekt fatal ist, wenn diese Assoziation bereits besteht)
Wenn ich jedoch ein Set<RecipeIngredient> benutze und sage, dass ein RecipeIngredient dem anderen gleicht, wenn der Zutatenname gleich ist müsste das funktionieren, dass ich die Assoziation nicht mehr doppelt habe. Leider hat das riesen Einfluss auf JPA http://stackoverflow.com/questions/5031614/the-jpa-hashcode-equals-dilemma
Hast du Erfahrung mit den Strategie dazu oder wie würdest du das lösen?


----------



## Tobse (23. Okt 2016)

In meinem Beispiel ist dieses Problem gelöst, indem ich die Zutat als eigene Entity modelliert habe. Wenn du dort den Namen als Primary-Key (@Id) deklarierst, sollte Hibernate damit zurechtkommen.

Du kannst ja auf deinem IngredientRepository auch eine Methode dafür anlegen:


```
public Ingredient findOrCreate(String ingredientName) {
    Ingredient existing = this.em.find(Ingredient.class, ingredientName);
    if (existing == null) {
        Ingredient newIngredient = new Ingredient(ingredientName);
        this.add(newIngredient);
        return newIngredient;
    } else {
        return existing;
    }
}
```


----------



## looparda (23. Okt 2016)

Ich habe eine Entity RecipeIngredient, die Ingredient, Unit und Amount beinhaltet. Dass Zutaten und Einheiten nur einmalig angelegt werden funktioniert bereits so wie implementiert, auch wenn ich das auf findOrCreate umstellen sollte. Die Assoziation ist momentan das Problem, weil ich das nicht wie bei findOrCreate per Namen finden kann.


----------



## Tobse (23. Okt 2016)

Dann solltest du kein Problem bekommen. Im JPA-Kontext ist ein _new_ allermeistens tatsächlich das erstellen einer neuen Entity. Du solltest _new Ingredient_ dann also nur schreiben, wenn du eine neue Zutat hinzufügen willst. Wenn du die Ingredients aus dem EntityManager benutzt, um die Relationen herzustellen, sollte alles klappen.


----------



## looparda (24. Okt 2016)

Ein Parser baut ein Recipe Objekt und gibt dieses zurück. Ist es valid wird es in die Datenbank gespeichert.
1.

```
tempIncredientList = extractIncredentsList(textFileContent);
for (int i = 0; i < tempIncredientList.size(); i++) {
    String tempName = cutString(tempIncredientList.get(i)[2],fieldLength);
    String tempUnit = cutString(tempIncredientList.get(i)[1],fieldLength);
    String tempAmount = cutString(tempIncredientList.get(i)[0],fieldLength);
    //         IngredientName, Amount, UnitName
    Unit unit = new Unit();
    unit.setName( tempUnit );
    unit = unitDAO.findOrCreate( unit, "title", tempUnit );
   
    Ingredient ingredient = new Ingredient();
    ingredient.setName(tempName);
    ingredient = ingredieDAO.findOrCreate( ingredient, "title", tempName );
   
   
    final RecipeIngredient recipeIngredient = new RecipeIngredient();

    recipeIngredient.setAmount( Double.valueOf(tempAmount) );
    recipeIngredient.setUnit( unit );
    recipeIngredient.setIngredient(ingredient);
    recipeIngredient.setRecipe(recipe);
    recipe.getRecipeIngredients().add(recipeIngredient);

}
```
Problem: Gehört dieses DAO Anfragen ob die Sachen schon in der Datenbank vorhanden sind wirklich in die Ebene eines Parsers? 
2.

```
tempIncredientList = extractIncredentsList(textFileContent);
for (int i = 0; i < tempIncredientList.size(); i++) {
    String tempName = cutString(tempIncredientList.get(i)[2],fieldLength);
    String tempUnit = cutString(tempIncredientList.get(i)[1],fieldLength);
    String tempAmount = cutString(tempIncredientList.get(i)[0],fieldLength);
    recipe.getRecipeIngredients().add( tempName, Double.valueOf(tempAmount), tempUnit);
}
```
Logik aus 1 in die Model-Klasse Recipe verschoben.
Problem: Mein Model interagiert nun mit sämlichen DAO.
Wo gehört sowas hin? Und warum muss ich während ich ein Model auf Business-Logik-Ebene konstruiere schon auf die Dantenbank zugreifen und sogar Entities anlegen?
Ich brauch unbedingt ein Referenzprojekt an dem ich mir was abschauen kann.


----------



## Tobse (24. Okt 2016)

Dein Parser sollte nicht mit der Datenbank Interagieren. Für diesen Anwendungsfall macht es sinn, dass du DTO-Klassen anlegst, die der Parser zurückgibt. Dein Service kann dann aus diesen DTO-Objekten in kooperation mit der DB Entities anlegen.


----------



## looparda (24. Okt 2016)

Parser war jetzt das einfachste Beispiel. Was soll das erst in der GUI werden? Das wird wieder ein wildes hinundhergemappe zwischen POJO/DTO und Model-Objekten. Da war ich schon mal. Ich blicke das Gesamtbild eines vollständigen Lifecycles eine Objekts (das wiederum Objekte aggregiert hat) von Datenbank in Applikation und vice versa nicht.


----------



## Tobse (25. Okt 2016)

Ich verstehe dein Problem leider nicht wirklich. Die GUI nimmt Enitäten entegegen und sie erstellt/modifiziert Entitäten. Ob deine Business-Logik die dann nacher verwirft oder nicht, ist doch ein völlig anderes Thema.


----------



## looparda (31. Okt 2016)

Ich bin inzwischen gut voran gekommen. Der Parser benutzt keine DTO/POJO sondern die Entities und übergibt sie an den Service. Der Service nimmt das Rezept nochmal auseinandern und versucht Entities aus der Datenbank wiederzuverwenden bei gewünschten Attributen.

```
public void create(Recipe recipe) {
    ...
    recipe.setSource( sourceDAO.findOrCreate( recipe.getSource(), "name", recipe.getSource().getName() ) );
    //analog für alle gewünschten Attribute und Collections
}
```
Auch für die GUI möchte ich versuchen die Entities zu benutzten. Die scheint nach Recherche auch möglich zu sein, da man eine detached Entity wieder attachen kann.

Außerdem konnte ich meinen GenericDAO sehr gut durch eine kleiner Änderung am Konstruktor zum Injecten mit Guice nutzen:

```
bind(new TypeLiteral<IGenericDAO<Cookbook, Integer>>(){}).to(new TypeLiteral<GenericDAOImpl<Cookbook, Integer>>(){}).in(Scopes.SINGLETON);
...
@Inject
private IGenericDAO<Cookbook, Integer> cookbookDAO;
```

Ich denke ich bin wieder auf Implementierungsebene angelangt und kann mich weiter mit JPA rumschlagen.


----------

