Hibernat Annotation in Interfaces

RelaX

Aktives Mitglied
Hallo,
ich würde gerne wissen ob es irgendwie möglich ist die ganzen Annotations in ein Interface zu verlagern.

Beispiel:

Code:
public interface Stift {

   public int getId();

}

@Entity
@Table(name = "Kugelschreiber")
public interface StiftHibernate extends Stift {

   @Id
   @Override
   public int getId();

}


public abstract class Kugelschreiber implements Stift {

   private int id;

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

   public int getId() {
      return id;
   }
}


public class KugelschreiberHibernate extends Kugelschreiber implements StiftHibernate {

}


Wie ließe sich so etwas machen? Das müsste doch Theoretisch irgendwie möglich sein.#


Vielen Dank!!
 

Tobse

Top Contributor
Nein, das geht nicht. Du musst eine Klasse mit Single-Table-Inheritance definieren; die Annotations für die Klassenmember musst du allerdings trotzdem noch in die richtigen Entity-Klassen schreiben.

Aber mal im Ernst: Was ist denn an den Annotations so schlimm? Wenn man die schön formatiert sind die IMHO super lesbar.
 

RelaX

Aktives Mitglied
Hallo Tobse,

vielen Dank für deine Antwort und sry für meine spät jetzt.

Also ich hatte anscheinend ein anderes Problem mit Hibernate, welches ich jetzt doch in den Griff bekommen hab.

Die Beschreibung einer Entität nur anhand eines Interfaces funktioniert anscheinend doch! Glaub meine Motivation hierfür fehlte noch und diese folgt jetzt :-D Meinungen dazu interessieren mich sehr!!!!

So hab ich jetzt die Möglichkeit eine konkrete Klasse zu erstellen ohne Annotationen und eine Klasse mit Annotationen(Hier "KugelschreiberHibernate"), welche meine Entitäten darstellen. Ich habe also beides sauber getrennt. Ich kann von meiner Basisklasse(Hier "Kugelschreiber") nun auch eine andere Implementierung erstellen ohne großen aufwand (Zum Beispiel "KugelschreiberDTO"). So ließe sich eine simple DTO von meiner Basisklasse erstellen. Die DTO verfügt zum Beispiel noch über eine weitere Variable und über ein Mappingmechanismus erspart man sich dann einen zusätzlichen Aufwand. Oder weiter, wäre es möglich ein JAXB-Object von meiner Basisklasse zu erstellen. Fehlt nun die Annotation zu JAXB, welche ebenfalls im Interface beschrieben wird (Zum Beispiel "KugelschreiberXML"). Das Mappen von einer Entität zu einem JAXB-Object, wäre hier auch ohne weiteren Aufwand direkt möglich. Da sich die beiden Objecte nur aufgrund des jeweiligen Interface unterscheiden. Die Interfaces dienen nun ausschließlich als Beschreibung für in diesem Beispiel JAXB und JPA. Das war meine Motivation dabei.
 

Tobse

Top Contributor
Ich kann mir das jetzt nicht konkret vorstellen. Hast du die Annotationen an die Interface-Methoden geschrieben? Wenn ja: würde ich persönlich garnicht wollen, das kann vmtl. böse nach hinten losgehen wenn man ein Feld später stärker einschränken möchte (weil die Setter für die Daten benutzt werden anstatt die Werte direkt in die Felder zu schreiben).

Man kann die Mapping-Definitionen glaub auch als XML in der persistence.xml hinterlegen.
 

RelaX

Aktives Mitglied
Genau das ist richtig. Für die Beschreibung der Entity werden die Getter-methoden herangzogen. Siehe auch das Interface aus meinem Beispiel. Diese hat auch die @Entity Annotation abbekommen.

Ich weiß jetzt nicht was du meinst mit stärker einschränken. Das macht mich neugierig! Sicher benutze ich immer die Getter und Setter für die Daten, wenn ich diese nicht über den Konstruktor festlege.

Mit dem Mapping meinte ich nicht die Definition für Hibernate oder so sondern das Mapping von Object der Klasse A zum Object der Klasse B.
 

Tobse

Top Contributor
Ich weiß jetzt nicht was du meinst mit stärker einschränken. Das macht mich neugierig! Sicher benutze ich immer die Getter und Setter für die Daten, wenn ich diese nicht über den Konstruktor festlege.

Folgendes Beispiel: Eine Entität hat ein Feld vom Typ int, dass nur positive Werte enthalten soll:
Java:
class Entity {
  private int feld;

  public int getFeld() { return feld; }
  public void setFeld(int value) {
    if (value <= 0) {
      throw IllegalArgumentException("feld must be positive");
    }
    this.feld = value;
  }
}

Jetzt sind in deiner DB folgende Daten:
Code:
|id |feld|
+---+----+
|  1|   1|
+---+----+
|  2|  50|
+---+----+
|  3| 113|
+---+----+

Jetzt kommt eine Anforderung hinzu, welche zur Folge hat, dass in diesem Feld keine Werte über 100 mehr stehen dürfen. Hat eine Instanz aber einen Wert über 100, soll 100 angenommen werden.

Der Code wird also geändert:
Java:
class Entity {
  private int feld;

  public int getFeld() {
    return feld > 100? 100 : feld;
  }
  public void setFeld(int value) {
    if (value <= 0) {
      throw IllegalArgumentException("feld must be positive");
    }
    if (value > 100) {
      throw IllegalArgumentException("feld must not be greater than 100");
    }
    this.feld = value;
  }
}

Wenn du jetzt per Hibernate die Entity #3 ausliest, fliegt eine IllegalArgumentException.
Es kann sein, dass das nur ein theoretisches Problem ist; praktisch ließe sich dieses Problem ja lösen, indem man beim Update einen Query ausführt: UPDATE entity SET feld = 100 WHERE feld > 100.

--------------------------
Mit dem Mapping meinte ich nicht die Definition für Hibernate oder so sondern das Mapping von Object der Klasse A zum Object der Klasse B.

Da hatte ich dich missverstanden. War es aber nicht dein Ursprüngliches Ziel, die Annotations aus der Entity zu bekommen? Oder ging es darum, dass sie an einem Interface sind?
 

stg

Top Contributor
Eigentlich will man normalerweise die Annotationen gar nicht an einem bestimmten Interface haben, sondern in der konkreten implementierenden Klasse. Das Interface, sofern sinnvoll und benötigt, ist ja gerade das, was man über die Grenzen hinaus teilt. Wenn du da deine JPA Annotationen hast, dann erzeugst du vollkommen unnötiger Weise eine starke Kopplung deiner restlichen Anwendung an die JPA Spezifikation. Dein Ziel ist nachvollziehbar, aber du versuchst an der komplett falschen Stelle und zu Lasten eines sauberen Designs zu sparen. Deine Trennung ist in Wirklichkeit keine, sondern wie eben beschrieben eher das genaue Gegenteil. Viel sinnvoller wäre etwa ein Interface Kugelschreiber, welches von KugelschreiberJPA und KugelschreiberJAXB implementiert wird. Die passenden Annotationen sind dann in den konkreten Klassen. Die entsprechenden POJOS erzeugst du aus dem Interface in deiner IDE mit einem einzigen oder wenigen Klicks, das ist kein Aufwand. Um das Mapping musst du dir auch keine großen Gedanken machen; da gibt es, gerade in solchen Standard-Fällen, auch viele gute Frameworks, bei denen das nur 1-2 Zeilen eigenen Code bedeuten. Es gibt natürlich wie immer für alles Ausnahmen, aber es sollte die Regel sein, dass Interface frei von Implementierungsdetails bleiben.
 

RelaX

Aktives Mitglied
Ersteinmal vielen Dank für die nette Antworten an dieser Stelle.

Jetzt folgt etwas längeres, also einmal kurz durchatmen, es lohnt sich sicherlich ;-)

Jetzt kann man das sehr schön Gegenüberstellen und sieht sehr deutlich den Unterschied:

Ein Interface IKugelschreiber

Eine Klasse Kugelschreiber implements IKugelschreiber

Eine Klasse KugelschreiberJPA extends Kugelschreiber
Hier geschieht die Beschreibung via Annotationen.

Eine Klasse KugelschreiberJAXB extends Kugelschreiber
Hier geschieht die Beschreibung via Annotationen.

Das heißt summiert haben wir 1 Interface und 3 Klassen.

Nach dem Beispiel von mir käme das dabei raus:

Ein Interface IKugelschreiber

Ein Interface IKugelschreiberJPA extends IKugelschreiber
Hier geschieht die Beschreibung via Annotationen.

Ein Interface IKugelschreiberJAXB extends IKugelschreiber
Hier geschieht die Beschreibung via Annotationen.

Eine Klasse Kugelschreiber implements IKugelschrieber

Eine Klasse KugelschreiberJPA extends Kugelschreiber implements IKugelschreiberJPA
(Hier geschieht nichts mehr) da diese Klasse nur für die Zusammenführung der Klasse und des Interfaces von Nöten ist.

Eine Klasse KugelschreiberJAXB extends Kugelschreiber implements IKugelschrieberJAXB
(Hier geschieht nichts mehr) da diese Klasse nur für die Zusammenführung der Klasse und des Interfaces von Nöten ist.

Also haben wir 3 Klassen und 3 Interfaces.

Jetzt wird es spannend und da liegt eben die Motivation drin. Es kommen jetzt zu x-Klassen die gleichen Werte hinzu. So hat jetzt jede Entity eine ID, einen User, welche diese Entity gespeichert hat und ein Datum wann das war.

Bei dem Enwurfsmuster mit nur einem Interface und einer Klasse, muss ich in alle x-Klassen in jede Entität die Annotationen reinschreiben. Das bedeutet das ich in all den Klassen folgendes hinzufügen muss:

private int ID ink. Getter & Setter
private int User ink. Getter & Setter
private int date ink. Getter & Setter

Zusätzlich muss ich die Annotationen in jeder Klasse hinzufügen.

private int ID ink. Getter & Setter
private int User ink. Getter & Setter
private int date ink. Getter & Setter

Das ist jede Menge Schreibarbeit bei 20 Klassen. Hinzu kommt dass es evt in den Annotationen mal zu Änderungen kommen kann. Oder nicht? Besteht überhaupt die Wahrscheinlichkeit? Falls ja, ist oder Wartungsaufwand sehr hoch da ja jede Klasse Ihre eigene Beschreibung hat.

In dem Beispiel von mir sähe das wie Folgt aus:

Jetzt kommt eine weitere "Abstrakte" Klasse hinzu.

Ein Interface IActiveRecord

Ein Interface IActiveRecordJPA
Hier geschieht die Beschreibung via Annotationen.

Ein Interface IActiveRecordJAXB
Hier geschieht die Beschreibung via Annotationen.

Eine Klasse ActiveRecord implements IActiveRecord
Hier steht die ID, der User und das Datum drin

Meine Bisherigen Klassen sehen nun wie folgt aus:

Ein Interface IKugelschreiber

Ein Interface IKugelschreiberJPA extends IKugelschreiber
Hier geschieht die Beschreibung via Annotationen.

Ein Interface IKugelschreiberJAXB extends IKugelschreiber
Hier geschieht die Beschreibung via Annotationen.

Eine Klasse Kugelschreiber extends ActiveRecord implements IKugelschrieber

Eine Klasse KugelschreiberJPA extends Kugelschreiber implements IKugelschreiberJPA, IActiveRecordJPA
(Hier geschieht nichts mehr) da diese Klasse nur für die Zusammenführung der Klasse und des Interfaces von Nöten ist.

Eine Klasse KugelschreiberJAXB extends Kugelschreiber implements IKugelschrieberJAXB, IActiveRecordJAXB
(Hier geschieht nichts mehr) da diese Klasse nur für die Zusammenführung der Klasse und des Interfaces von Nöten ist.

Was ist mit den Bereits existierenden Klassen passiert?

Die Hauptklasse hat nun von der ActiveRecordklasse geerbt. Das heißt an dieser Stelle wurden alle Getter&Setter hinzugfügt. Vererbung halt ;-)

Die Konkrete Klasse also JPA und JAXB haben jeweils das passende Interface hinzubekommen.

Also der Aufwand der beiden Entwurfsmuster unterscheidet sich schon beim Aufbau sehr stark voneinander.

Angenommen ich ändere jetzt etwas an den Gemeinsamkeiten der x-Klassen. So soll nun ein Datum hinzu kommen welche ein Datum der letzten Änderung beinhaltet.

Beim ersten Entwurfsmuster muss ich ja ALLE x-Klassen öffnen und folgendes hinzufügen:

private Date updated_at inkl. Getter&Setter
Annotationen dazu

Beim zweiten Entwurfsmuster füge ich einzig und alleine einer Klasse diese Daten hinzu.
 

Ähnliche Java Themen

Neue Themen


Oben