# jdbc datenbankbooster



## tk-germany (29. Februar 2008)

Hallo Zusammen,
ich möchte gern einen DatenbankBooster erstellen.
Grundgedanke, ein Programm dem man 2 Datenbanken und deren Typ ("sybase" oder "oracle") nennt, dazu 2 Tabellen und die Spalten.
Anhand dessen soll das Programm von Datenbank1.Tabelle1.Spalten1() nach Datenbank2.Tabelle2.Spalten2() kopieren.
Da das Programm dann jedoch in diversen Fachbereichen angewandt werden soll, sollte es so allgemein wie möglich gehalten sein. Daher auch die einzelnen inputwerte die mit Hilfe einer Konfigurationsdatei übergeben werden, der Name der Konfigdatei ist der Startparameter des Programmes, sodaß man Ihm bei jedem Start eine neue Konfiguration überhelfen kann.

Mein Problem nun:
bei JDBC nutzt man, wenn ich das als Neuling richtig verstanden habe für einen Datenbankinsert ja preparedstatements.
Die Statements bekomme ich auch schon richtig prepared, variabel, je nach Anzahl der Spalten etc.

Aber dann gibt man dem Statement ja die Werte via 
<statement>.set<Datentyp>(<nr>, <wert>)

Jedoch kenne ich den Datentyp ja garnicht im Voraus, wie kann ich realisieren, das das Programm trotzdem variabel bleibt und alle Datentypen "kopieren" kann?

Eine weitere Frage ist, bei Sybase und Oracle gibt es zum Teil ja verschiedene Datentypen, kann ich irgendwie absichern, das QuellZelle wirklich in ZielZelle paßt oder kann ich das nur per Try{} und catch{} abfangen?

Vielen Dank und viele Grüsse,
Thomas


----------



## procurve (29. Februar 2008)

mit instanceOf() kannst du per if sämtliche vorkommenden Datentypen überprüfen und die entsprechenden set<Datentyp>-Methoden aufrufen.


----------



## tk-germany (29. Februar 2008)

Vielen Dank,
also wenn ich das richtig verstehe muss ich mit:

if(obj instanceof String) insert.setString(i+1, obj);
else if(obj instanceof Integer) insert.setInt(i+1, obj);

alle Datentypen durchnudeln, oder?

und wie bekomme ich bei der Abfrage den genauen Datentyp?
weil da muss ich ja auch den Datentyp für getString etc. wissen, aber wie erhalte ich den ohne vorher die Abfrage zu machen?


----------



## procurve (29. Februar 2008)

Andersrum geht es so:


```
if (String.class.isAssignableFrom(<deine Variable hier>.getClass())) {
  blablaString();
} else if (Integer.class.isAssignableFrom(<deine Variable hier>.getClass())) {
  blablaInteger();
}
```


----------



## Anime-Otaku (29. Februar 2008)

Also um den Datentyp der Zielspalte zu ermitteln hilft dir das weiter:
http://www.tutorials.de/forum/java/...-und-dementsprechend-string-konvertieren.html

Hier gibt es ein schönes Mapping des JDBC Types auf Java und Oracle Type
http://download.oracle.com/docs/html/A64685_01/basic3.htm


----------



## tk-germany (29. Februar 2008)

procurve hat gesagt.:


> Andersrum geht es so:
> 
> 
> ```
> ...



Was meinst Du mit "andersrum"?
Was genau "tut" String.class.isAssignableFrom?



Anime-Otaku hat gesagt.:


> Also um den Datentyp der Zielspalte zu ermitteln hilft dir das weiter:
> http://www.tutorials.de/forum/java/...-und-dementsprechend-string-konvertieren.html
> 
> Hier gibt es ein schönes Mapping des JDBC Types auf Java und Oracle Type
> http://download.oracle.com/docs/html/A64685_01/basic3.htm



Vielen Dank für die Mappings, das ist Super 
Wenn ich aus den Metadaten der Quelltabelle die Datentypen auslese (mit: sqlrsmd.getColumnType(Spaltennummer)) erhalte ich Zahl, wie bekomme ich dann raus welchem Datentyp aus dem Mapping das entspricht?

Und noch eine sicherlich relativ dumme Frage:
wie gehe ich am besten mit benutzerdefinierten Datenfeldtypen um? 
Einfach als String behandeln?


----------



## procurve (29. Februar 2008)

tk-germany hat gesagt.:


> Was meinst Du mit "andersrum"?
> Was genau "tut" String.class.isAssignableFrom?



1. Frage:
Andersrum meint in diesem Fall, das Gegenteil von deiner ursprünglichen Frage, die mit instanceof gelöst wurde. 

2. Frage:
Das würde ein Blick in die API klären... http://java.sun.com/javase/6/docs/api/java/lang/Class.html#isAssignableFrom(java.lang.Class)
Die Funktion liefert dann true zurück, wenn das zu überprüfende Objekt (<Deine Variable>.getClass()) eine Kindklasse von String ist. (D.h. entweder ist die Klasse String, oder eine von dir definierte Klasse z.B. class MyString extends String)

Etwas wirr, aber ich hoffe, du steigst da durch. 

Ansonsten scheinen die von Anime-Otaku geposteten Mappings dein Problem eleganter zu lösen.


----------



## Anime-Otaku (29. Februar 2008)

Diese Zahl die du bekommst entspricht dem JDBC Datentyp, den du über Types.X abfragen kannst....


```
switch (sqlType) {
			case Types.CHAR:
			case Types.LONGVARCHAR:
			case Types.VARCHAR:
				preparedStatement.setString(count, value);
				break;
			case Types.FLOAT:
			case Types.DOUBLE:
				preparedStatement.setDouble(count, Double.parseDouble(value));
				break;
			case Types.BIT:
				preparedStatement.setBoolean(count, Boolean.parseBoolean(value));
				break;
			case Types.TINYINT:
				preparedStatement.setByte(count, Byte.parseByte(value));
				break;
			case Types.SMALLINT:
				preparedStatement.setShort(count, Short.parseShort(value));
				break;
			case Types.INTEGER:
				preparedStatement.setInt(count, Integer.parseInt(value));
				break;
			case Types.BIGINT:
				preparedStatement.setLong(count, Long.parseLong(value));
				break;
			case Types.REAL:
				preparedStatement.setFloat(count, Float.parseFloat(value));
				break;
			case Types.NUMERIC:
			case Types.DECIMAL:
				preparedStatement.setBigDecimal(count, new BigDecimal(value));
				break;
			default:
				throw new ConfigurationException(
						"unsupported SQL data type, no converting available for int type: " + sqlType);
		}
```


----------



## tk-germany (29. Februar 2008)

Großartig, ihr seid echt klasse, vielen Dank

Tut mir leid, wenn meine Fragen etwas doof waren, bin leider Java Neuling und versuch mir das grad selbst anzueignen und glaub auch, dass ich mich dazu mit dem Projekt etwas übernommen habe und muss sagen, für Neulinge die aus der Prozeduralen Pascalwelt kommen, is Java echt der blanke HORROR!

Auf der einen Seite habt Ihr meinen Glauben an die Welt gerettet und das es doch noch Menschen gibt, die einem helfen.
Auf der anderen Seite habt Ihr soeben mein Wochenende zerstört, weil ich jetzt wohl das ganze WE versuchen werd weiterzukommen mit dem Projekt.

Auf jeden Fall aber: herzlichsten Dank


----------



## procurve (29. Februar 2008)

In dieser Form zerstöre ich immer wieder gerne Wochenenden. 
Wenn du Fragen hast, einfach wieder hier an's Forum wenden, es gibt hier viele, die einem schnell helfen.


----------



## Thomas Darimont (29. Februar 2008)

Hallo,

schau mal hier:
http://sourceforge.net/projects/dbcopyplugin/

Gruß Tom


----------



## tk-germany (3. März 2008)

Hallo zusammen,
also ich bin schonmal ein ganzes Stückerl weiter gekommen, dank euch.
Aber ein paar Fragen existieren nachwievor:

1. Gibt es eine Klasse etc, der ich einfach eine Type-Nummer gebe und sie gibt mir als String den Type-Namen zurück?

2. welchen Datentyp nehme ich am besten um ohne Probleme alle Arten von Datentypen aus der Datenbank einlesen zu können. 
Ich habe Object gewählt, ist das eurer Meinung die richtige Wahl?

3. wie kann ich problemlos die verschiedensten Arten von Datumsfeldern aus 2 verschiedenen Datenbanksystemen auslesen, bzw. in sie schreiben?
Die Systeme sind, wie oben genannt Oracle und Sybase und es gibt Datumsfelder mit Uhrzeit, ohne Uhrzeit und in verschiedenen Anordnungen von Tag, Monats und Jahresfeldern...

Derzeit habe ich dazu in die Konfigdatei einen String eingebaut:
#srcdateformat, Format der Datumsfelder der Quelldatenbank
srcdateformat="yyyy-MM-dd HH:mm:ss.S"
#tardateformat, format der Datumsfelder der Zieldatenbank (hier Sybaseangabe)
tardateformat="112"

Das Datum lese ich wie folgt aus:
case Types.DATE:           
	SimpleDateFormat format = new SimpleDateFormat(konfig.srcdateformat);
	try {
		ausgabe = format.parse(sqlresult.getString(Spalte));
	} catch (ParseException e) {
		System.out.println("Fehler bei der Datumsumwandlung: " + e);
	}

und schreibe es so:
case Types.DATE:           
	sqlprestmt.setDate(Spalte, (Date)setdata);
	break; 

Jedoch erhalte ich:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.sql.Date
Aber ich hab es doch vorher umformatiert...!?

auch wenn ich es mit 
ausgabe = sqlresult.getDate(Spalte); 
auslese erhalte ich diese Fehlermeldung (btw: ausgabe = Object)

In beiden Fällen enthält ausgabe den Wert: 2007-04-12 04:01:18.0 in der gleichen Formatierung.

Wäre toll, wenn Ihr mir auch hier helfen könntet, vielen Dank und Gruss,
Thomas


----------



## Anime-Otaku (3. März 2008)

1. Du kannst statt dem int auch den JDBC Namen holen resultSetMetaData.getColumnTypeName(1), aber mehr nicht (soviel ich weiß), da du ja auf der JDBC Ebene arbeitest und die Oracle Datentypen dazu nicht kennst (außer es gibt ne spezielle Oracle lib vielleicht^^)

2. Object hört sich gut an^^

3. Du bekommst ja beim auslesen kein String zurück, sondern ein sql.Timestamp, Time oder Date....von daher verstehe ich nicht ganz, was du dann mit den String hantieren willst.
Es gibt Datentypen, da hat man halt nur ein Datum (Date) oder eine Zeit (time) oder einen kompletten Zeitstempel mit Datum und Zeit  (timestamp)


----------



## tk-germany (3. März 2008)

Huhu 
1. na danke, paßt doch, grummel, das hätt ich wohl auch selber finden können, aber als einsteiger überrennt einen die pure Fülle von Java 

2. klasse, immerhin ein was, was ich von allein richtig gemacht hab! 

3. und wie helf ich das der Zieldatenbank dann wieder über?
weil wie gesagt: sqlprestmt.setDate(Spalte, (Date)setdata); will er auch nich, 
er mäkelt an: java.lang.String cannot be cast to java.sql.Date 
setdata ist ein Object und die Ausgabe von:
SimpleDateFormat format = new SimpleDateFormat(konfig.srcdateformat);
ausgabe = format.parse(sqlresult.getString(Spalte));

und zu:
sqlprestmt.setDate(Spalte, setdata);
sagt er:
The method setDate(int, Date) in the type PreparedStatement is not applicable for the arguments  (int, Object)

Danke schonmal


----------



## Anime-Otaku (3. März 2008)

Er weiß ja nicht von welchem Datentyp, das Object ist. Daher musst du erstmal mittels ifs und instanceof Unterscheiden welches Objekt  du bekommst und dementsprechend es setzen und casten.


----------



## tk-germany (3. März 2008)

kannst Du mir nen kleinen Codeschnippsel dazu geben?


----------



## zerix (3. März 2008)

Hallo,


```
if(obj instanceof String){
   String s = (String)obj;
   //mach was
}
```


```
if(String.class.isAssignableFrom(obj.getClass())){
   String s = (String)obj;
   //mach was
}
```

MFG

Sascha


----------



## Anime-Otaku (3. März 2008)

Daran siehst du....aha das ist ein String....wenn du weißt, dass du nur Text darin speicherst, kannst du gleich setString machen. 

*Wenn *du aber die Stringklasse auch für Zahlen zum speichern verwendest (oder sonst was), musst du hier noch auf die Zieldatentyp unterscheiden und dementsprechend konvertieren.


----------



## tk-germany (3. März 2008)

Hallo Sascha,
vielen Dank, so erhalte ich aus meinem Object einen String, aber ich brauche ein Date oder irgendwie vermute ich mittlerweile, dass ich was ganz anderes brauche.

auch dieser Code:
Date datum = (Date) setdata;
sqlprestmt.setDate(Spalte, datum);

erzeugt den Fehler:
Lese Quelle.Spalte3(Type91)=2007-04-12 04:01:18.0...
Schreibe Ziel.Spalte3... Spaltentyp=91(DATE)
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.sql.Date

Habe noch das hier gefunden:
http://java.sun.com/j2se/1.3/docs/guide/jdbc/getstart/statement.html



> d, t, and ts for date and time literals DBMSs differ in the syntax they use for date, time, and timestamp literals. The JDBC API supports ISO standard format for the syntax of these literals, using an escape clause that the driver must translate to the DBMS representation. For example, a date is specified in a JDBC SQL statement with the following syntax:
> {d 'yyyy-mm-dd'}
> 
> In this syntax, yyyy is the year, mm is the month, and dd is the day. The driver will replace the escape clause with the equivalent DBMS-specific representation. For example, the driver might replace {d 1999-02-28} with '28-FEB-99' if that is the appropriate format for the underlying database. There are analogous escape clauses for TIME and TIMESTAMP:
> ...



"The driver will replace the escape clause with the equivalent DBMS-specific representation"
Das klingt ja eigentlich ganz gut, aber ich hab absolut keinen Plan wie ich das in die Tat umsetzen kann.


----------



## Anime-Otaku (3. März 2008)

Vielleicht liegt es daran, das setdata ein String ist.....und somit nicht im geringsten was mit einem Date Objekt zu tun hat.

Die einfachste Variante ist, dass du aus dem Resultset der Quelldatenbank ein Date herausholst und dieses dann in die neue Datenbank steckst.

Oder du musst den Datumstring parsen, mit SimpleDateFormat und den java.util.Date nach java.sql.Date konvertieren.


----------



## zerix (3. März 2008)

Das ganze was ich dir mit String gezeigt hab, kannst du mit jeder Klasse machen. 



```
Date datum = (Date) setdata;
```

Das kannst du nicht machen, wenn das obj was du hast ein String ist, dann musst du es einem String zuweisen und nicht einem Date-Object.

Ein Rat von mir ist mal, dass du dir mal die Grundlagen von Java anschaust.

MFG

Sascha


----------



## tk-germany (3. März 2008)

Mist, Entwarnung, ES TUT MIR LEID 
es funktioniert prima, ich hatte nur, nachdem ich alles je nach Datentyp auslese ganz unten, vergessen ein 
ausgabe = sqlresult.getString(Spalte); 
auszukommentieren, und somit ist egal was ich eingelesen habe am Ende doch wieder ein String gewesen, so kann man sich selbst zum Narren machen, sorry 


Zwei Fragen hab ich aber doch noch:
1. ich kann getBigDecimal machen, aber es gibt kein setBigDecimal ... wie bekomme ich den Wert "sauber" wieder in die Datenbank?
2. ich lese die Werte selektiert nach dem Spaltentyp aus:

```
case Types.TIME:           
	ausgabe = sqlresult.getTime(Spalte);         
	break;   
case Types.TIMESTAMP:           
	ausgabe = sqlresult.getTimestamp(Spalte);         
	break;   
case Types.DATE:           
	ausgabe = sqlresult.getDate(Spalte);
	break;
```
Allerdings erhalte ich so beim Datumsfeld nur das Datum, wenn ich es als String auslese auch noch die Zeit,
wie kann ich es als Date auslesen und trotzdem die Zeit erhalten?


----------

