# Mit JAXB erzeugte Klassen persistieren (Hibernate)



## pocketom (7. Sep 2009)

Wir generieren unsere Klassen aus XML Schema Dateien. Jede Klasse wird im XML Schema einmal abstrakt definiert, mit allen Membervariablen. Davon wird eine (meist "leere") Ableitung erzeugt der dann (ausschliesslich) Methoden & Logic hinzugefügt werden. Mittels
[XML]
<xsd:annotation>
	<xsd:appinfo>
		<jaxb:class name="Document" ref="com.ourpackage.OurClass"/>
	</xsd:appinfo>
</xsd:annotation>
[/XML]
wird der JAXB Java Generator dazu bewegt bereits vorhandene konkrete Klassen nicht mehr zu überschreiben da dort ja Funktionalität angehängt wird. Die abstrakten Klassen jedoch werden jedes Mal neu generiert und die alten werden überschrieben. Das ganze ist mit Maven verknüpft und funktioniert so ganz wunderbar, solange bis Hibernate ins Spiel kommt. 

Denn jetzt brauchen wir ja Hibernate Annotations über den Definitionen der Membervariablen. Die Member befinden sich jedoch nur in den abstrakten Klassen welche ja immer wieder überschrieben werden.

Hat irgendwer eine gute Idee wie man das geschickt regeln kann? Kann man die Hibernate Annotationen bereits in die XML Schemas einbringen so dass sie automatisch über den Membervariablen in den generierten Klassen auftauchen?


----------



## Noctarius (7. Sep 2009)

So ganz einfach ist das nicht. Generell ist das eigentliche Problem, dass das Maven Plugin die JAXB Klassen bei jedem Compile erneut erstellt.

Es gibt aber eine Lösung und zwar über Interfaces:

```
@XmlJavaTypeAdapter(FooAdapter.class)
public interface IFoo { // Interface mit XmlAdapter
    public int getId();
    public void setId(int id);

    public String getValue();
    public void setValue(String value);
}

@Entity
@Table("foo")
public class FooImpl implements IFoo { // Hibernate Klasse
    @Id int id;
    String value;

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

    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "foo")
public class Foo { // JAXB Klasse
    @XmlAttribute(required = true)
    protected int id;
    @XmlAttribute(required = true)
    protected String value;

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

    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }
}

public class FooAdapter extends XmlAdapter<Foo, IFoo> {
    @Override
    public Foo marshal(IFoo v) throws Exception {
        Foo foo = new Foo();
        foo.setId(v.getId());
        foo.setValue(v.getValue());
        return foo;
    }

    @Override
    public IFoo unmarshal(Foo v) throws Exception {
        IFoo foo = new FooImpl();
        foo.setId(v.getId());
        foo.setValue(v.getValue());
        return foo;
    }
}
```

Sehr umständlich aber Wirkungsvoll


----------



## pocketom (7. Sep 2009)

Ok, klingt wirklich sehr umständlich. Wenn ich nun die abstrakte Basisklasse um sagen wir eine Variable erweitere, dann müsste ich die Änderung auch in den anderen nachtragen oder? Bzw. in der Hibernateklasse wird ja jede Variable nopchmal deklariert oder?


----------



## Noctarius (7. Sep 2009)

Genau das. War nach langem Suchen die einzige funktionierende Lösung da die Annotations sich nicht sauber vererben.


----------



## pocketom (7. Sep 2009)

Ok, das heisst selbst wenn ich die Annotations dauerhaft in die abstrakten Klassen bekommen würde (z.B. PostProcessing Script in der Maven Phase process-sources anwenden), so könnte ich die Ableitungen davon nicht gescheit persistieren, verstehe ich das so richtig? Woran scheitert es da?


----------



## Noctarius (7. Sep 2009)

Annotations benötigen selbst das Annotation @Inherited um vererbt zu werden. Dies gilt dann aber leider nur für Annotations in Klassen und nicht in Interfaces.

Abstract Classes brauchst du hier auch nicht. Wenn die Annotations sauber vererbt werden würden (bei mir kam noch das Problem mit dem Interface-driven hinzu) könntest du von den JAXB Klassen ableiten und die Hibernate Annotations in der abgeleiteten Klasse hinterlegen. Aber auch hier wäre das Problem, dass du alle Getter überschreiben müsstest um die Annotations sauber unterzubringen. Generell ist JAXB mit JPA / Hibernate / Eclipselink recht unhandlich.

Wenn du aber das obige einmal gebaut hast (und deine Klassen nicht alle 2 Tage Änderungen erfahren - hier wäre dann eh irgendwie ein Designfehler oder schlechte Planung im Spiel) ist es ganz sauber nutzbar. Halt immer das Interface benutzen und der Adapter kümmert sich bei JAXB ums Konvertieren vor der Serialisierung.


----------



## Noctarius (7. Sep 2009)

Du könntest noch versuchen das Mapping für Hibernate komplett in xml zu Verfassen, dass habe ich zugegebener Maßen nie probiert, da ich kein Freund davon bin. Das Problem daran ist dann aber, dass die Klassen vermutlich nicht mehr in JAXB funktionieren, da Hibernate eine perfide Liebe zu Proxies hat.


----------



## velaluka (7. Sep 2009)

Hallo,
wie siehts hier mit aus....Noch nie selber verwendet, aber zum Glück wieder eingefallen
Ciao velalulka


----------



## Noctarius (7. Sep 2009)

Ich vermute, dass HyperJaxb das oben genannte einfach nur automatisch erzeugt und nutzt. Was die Sache aber natürlich angenehmer macht.

Problem bei mir war damals, ich brauchte EclipseLink 

Edit:
Ok nope, HyperJaxb packt JPA und JAXB Annotations zusammen in eine Klasse (und baut direkt XML-Workarounds für JAXB ein). Gibt es auch direkt als Maven Plugin.


----------



## pocketom (7. Sep 2009)

HyperJAXB klingt echt ein bischen wie die eierlegende Wollmilchsau zu diesem Thema :-D

Ich werds ausprobieren...!


----------



## pocketom (8. Sep 2009)

So, maven-hyperjaxb3-plugin installiert und zum laufen gebracht. Sieht auf den ersten Blick ganz gut aus. Zu jeder generierten Java Klasse werden jetzt auch Hibernate Annotations hinzugefügt. Vorläufig einziges Problem:

Er generiert die Annotations sowohl für die abstrakten als auch die konkreten Klassen:


```
@Entity(name = "oneit.AbstractExchangeableBusinessObject")
@Table(name = "ABSTRACTEXCHANGEABLEBUSINESS_0")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class AbstractExchangeableBusinessObject implements Equals, HashCode
{
...
...
...
}
```


```
@Entity(name = "oneit.ExchangeableBusinessObject")
@Table(name = "EXCHANGEABLEBUSINESSOBJECT")
public class ExchangeableBusinessObject extends AbstractExchangeableBusinessObject implements Equals, HashCode
{
...
...
...
}
```

Was muss ich denn nun persistieren? 2 Tables könnens ja in dem Fall auch kaum sein da man die abstrakten Klassen ja eh nicht instanziieren kann... ???


----------

