# JPA coloumnDefinition zur Laufzeit ändern



## muRuS (15. Apr 2014)

Hallo Leute,

hoffe bin im richtigen Unterforum. =) Ich habe eine Anwendung, die auf eine Datenbank zugreift. Diese kann entweder auf einem PostgreSQL Server oder MS-SQL Server liegen. Ich möchte nun beim Login auswählen, um welche Art sich der Server dabei handelt. Das Problem ist, dass bei PostgreSQL BYTE-Felder als bytea (@Column(columnDefinition="bytea") private byte[] test) abgespeichert werden und bei MS-SQL als VARBINARY(8000) (@Column(columnDefinition="VARBINARY(8000)") private byte[] test). Ich würde halt gerne eine Variable dort setzen (columnDefinition=serverArt), aber die muss leider final sein. Beim Laden des Logins werden im Prinzip ja schon die Entity-Klassen geladen, auch wenn man noch nicht mit dem Server verbunden ist. Gibt es Kniffe, um das doch noch zur Laufzeit wählen zu können und man diese Klassen einfach "nachladen" kann?

Danke im Voraus :applaus:
muRuS


----------



## Barista (15. Apr 2014)

Ich habe kurz nachgelesen, JPA arbeitet mit Gettern und Settern.

Du könntest also ein Datenbank-Typ-übergreifendes Interface definieren und je nach DB die entsprechenden Klassen verwenden.

Im Anwendungscode wäre der DB-Typ zumindest ausserhalb von DAO-Klassen / Repository-Klassen vom Typ der DB unabhängig.

Ich weiß nicht, ob dann noch weitere Probleme auftauchen.

Musst Du einfach ausprobieren.


----------



## muRuS (16. Apr 2014)

Erstmal danke. Klingt interessant. Aber ich weiss jetzt nicht ob ich das richtig verstanden habe. Ich kopiere zunächst meine Entitäts-Klasse und habe dann zweimal die selbe Klasse. Einmal mit bytea und einmal mit VARBINARY. Diese implementieren dann mein Interface, das alle Methoden dieser Klasse beinhaltet. Ich müsste im gesamten Code dann statt auf die Entität selbst auf dieses Interface zugreifen, richtig? Wie zwinge ich dann das Programm die eine oder die andere Klasse zu verwenden. Steh grad aufm Schlauch.


----------



## Barista (16. Apr 2014)

Wenn Du DAO-Klassen oder Repsoitory-Klassen hast, dann haben diese zum Beispiel eine readEntity-Methode

JPA (Java Persistence API) - Torsten Horn


```
public <T> T readEntity( Class<T> clss, Object id )
   {
      EntityManager em = emf.createEntityManager();
      try {
         return em.find( clss, id );
      } finally {
         em.close();
      }
   }
```

Als Parameter clss übergibts Du die jeweilige Klasse, in einem if entscheidest Du, welche Klasse.

Später kannst Du das noch schön machen mit einer Factory.


----------



## muRuS (16. Apr 2014)

Hi,

danke für den Link. Da stehen einige praktische Tipps drin. 
Jetzt weiss ich was du meinst. Das wird aber nicht klappen. Ich habe rausgefunden, dass es zunächst egal ist, was bei der columnDefinition drin steht, wenn die Tabellen bereits existieren. Erst wenn man auf eine leere Datenbank verbindet und mittesl create-tables in der persistence.xml die neuen Tabellen von JPA erstellen lassen möchte, dann "guckt" er auf die columnDefinition und wirft dann einen Fehler (zb. VARBINARY existiert nicht bei PostgreSQL). Sprich es hat nicht mit den Gettern und Settern zutun. Das Getten und Setten mache ich ja erst später zur Laufzeit. Ich wüsste echt nicht wie man das dann anstellen soll. Schade dass man MSSQL den Datentyp "bytea" oder PostgreSQL den Datentyp "VARBINARY()" nicht "beibringen" kann. Oder? :bahnhof:


----------



## Barista (16. Apr 2014)

> Jetzt weiss ich was du meinst. Das wird aber nicht klappen. Ich habe rausgefunden, dass es zunächst egal ist, was bei der columnDefinition drin steht, wenn die Tabellen bereits existieren. Erst wenn man auf eine leere Datenbank verbindet und mittesl create-tables in der persistence.xml die neuen Tabellen von JPA erstellen lassen möchte, dann "guckt" er auf die columnDefinition und wirft dann einen Fehler (zb. VARBINARY existiert nicht bei PostgreSQL).



Du musst in einer Konfiguration(-sdatei) oder über einen Start-Parameter den Typ der Datenbank an das Programm übergeben.

Eine Alternative wäre das Auslesen der Database-Metadata:

JDBC DatabaseMetaData.getDatabaseProductName

Dafür kannst Du aber nicht JPA nutzen (ich kenne das aber nicht so genau, dass ich Dir sagen kann, ob es da auch so einen Aufruf gibt) sondern musst direkt JDBC benutzen.


----------



## muRuS (16. Apr 2014)

Hi,

das funktioniert leider alles nicht. Wenn ich statt @Column(columnDefinition = "bytea") so etwas schreibe wie @Column(columnDefinition = TestApp.bytetype) und die TestApp etwa so einen Inhalt hat:

```
static public String bytetype;

public static void main(String[] args) {

        if(args.length==1 && args[0].equals("-postgresql")){
            TestApp.bytetype = "bytea";
        }else{
            TestApp.bytetype = "VARBINARY(8000)";
        }
        launch(TestApp.class, args);
    }
```

...dann sagt er mir, dass bytetype halt final sein soll. Wenn ich das mache, dann muss ich logischerweise bei der Deklaration den Wert schon angeben und kann nicht erst beim Start den Parameter prüfen und dann eine finale Variable initalisieren. Ist halt final. Wie ich es auch drehe und wende. Es will wohl einfach nicht. Ich kann den bytetype halt nur hardcoded setzen und zwei Programme bauen. Eins für Postgre und eins für MSSQL. 

Ist aber auch nicht ganz so schlimm. 
Trotzdem danke für die Hilfe.

muRuS


----------

