Ahoi zusammen,
kann mir jemand ein funktionierendes Beispiel nennen, wie ich ein leeres Blob in ein PreparedStatement eintrage?
Ich mache gerade folgendes: ich versuche eine Klasse zu schreiben, die generisch Tabellen aus einer Datenbank in eine andere überträgt. Der zugriff erfolgt über JDBC, aber ich kann weder vorhersehen, um welches Datenbanksystem es sich zur Laufzeit handeln wird, noch den Aufbau und Inhalt der Tabellen. Ich bekomme zwei Connections übergeben, eine zum Lesen und eine zum Schreiben, und los geht's. Zunächst ermittle ich die Tabellen und den Tabellenaufbau, anschließend lese ich jeweils aus einer Tabelle die Daten ein und schreibe sie in eine andere Tabelle. Die Tabellen können Blobs enthalten, ggf. auch mehrere je Zeile.
Hierfür verwende ich ein PreparedStatement, das für jede Tabelle einmal angelegt wird und für jede Zeile dann neu bestückt wird. Das klappt auch alles ganz prima, aber wenn ich bei der Quelltabelle auf ein Blob-Feld ohne Inhalt stoße, dann knallt es immer beim Schreiben. Ich habe schon alles mögliche versucht, wie ich das leere Blob da eintragen könnte, zum Beispiel setBlob(null) oder setBinaryStream mit unterschiedlichen Streams, aber irgendwo kracht es immer, meistens mit einer NullPointerException. Es funktioniert nur, wenn ich in einem solchen Fall mindestens ein Byte in das Ziel-Blob eintrage. Aber das ist natürlich inhaltlich nicht korrekt, das Ziel-Blob soll schließlich NULL sein.
Deshalb möchte ich mal rumfragen, ob mir jemand den korrekten Weg nennen kann, ein leeres Blob in ein PreparedStatement einzutragen. Vielen Dank für jeden Tipp. Und schönes Wochenende.
NBK
- Hier zum Anschauen mal die ganze Klasse. Momentan schreibe ich die Blobs in ein Temporärverzeichnis raus und lese sie von dort wieder ein; wenn sie leer sind, schreibe ich eine Null in das Zielblob, was wie gesagt nicht korrekt ist, aber wenigstens funktioniert. Wer sich's ansehen möchte, sollte bei der Methode copyData() anfangen.
kann mir jemand ein funktionierendes Beispiel nennen, wie ich ein leeres Blob in ein PreparedStatement eintrage?
Ich mache gerade folgendes: ich versuche eine Klasse zu schreiben, die generisch Tabellen aus einer Datenbank in eine andere überträgt. Der zugriff erfolgt über JDBC, aber ich kann weder vorhersehen, um welches Datenbanksystem es sich zur Laufzeit handeln wird, noch den Aufbau und Inhalt der Tabellen. Ich bekomme zwei Connections übergeben, eine zum Lesen und eine zum Schreiben, und los geht's. Zunächst ermittle ich die Tabellen und den Tabellenaufbau, anschließend lese ich jeweils aus einer Tabelle die Daten ein und schreibe sie in eine andere Tabelle. Die Tabellen können Blobs enthalten, ggf. auch mehrere je Zeile.
Hierfür verwende ich ein PreparedStatement, das für jede Tabelle einmal angelegt wird und für jede Zeile dann neu bestückt wird. Das klappt auch alles ganz prima, aber wenn ich bei der Quelltabelle auf ein Blob-Feld ohne Inhalt stoße, dann knallt es immer beim Schreiben. Ich habe schon alles mögliche versucht, wie ich das leere Blob da eintragen könnte, zum Beispiel setBlob(null) oder setBinaryStream mit unterschiedlichen Streams, aber irgendwo kracht es immer, meistens mit einer NullPointerException. Es funktioniert nur, wenn ich in einem solchen Fall mindestens ein Byte in das Ziel-Blob eintrage. Aber das ist natürlich inhaltlich nicht korrekt, das Ziel-Blob soll schließlich NULL sein.
Deshalb möchte ich mal rumfragen, ob mir jemand den korrekten Weg nennen kann, ein leeres Blob in ein PreparedStatement einzutragen. Vielen Dank für jeden Tipp. Und schönes Wochenende.
NBK
- Hier zum Anschauen mal die ganze Klasse. Momentan schreibe ich die Blobs in ein Temporärverzeichnis raus und lese sie von dort wieder ein; wenn sie leer sind, schreibe ich eine Null in das Zielblob, was wie gesagt nicht korrekt ist, aber wenigstens funktioniert. Wer sich's ansehen möchte, sollte bei der Methode copyData() anfangen.
Java:
public class JdbcMoverSimple implements JdbcMover {
private Connection sourceconnection;
private Connection destinationconnection;
private String workdir;
private boolean sourcetablesAnalyzed = false;
private final int MAXVARCHAR = 4000;
private final List<TableData> tables = new ArrayList<TableData>();
public class TableData {
private String tableName;
private final List<ColumnData> tableColumns = new ArrayList<ColumnData>();
public String getTableName() {
return this.tableName;
}
public void setTableName(final String tableName) {
this.tableName = tableName;
}
public List<ColumnData> getTableColumns() {
return this.tableColumns;
}
public boolean isSQLCompatible() {
boolean result = true;
for (final ColumnData columndata : this.getTableColumns()) {
result = result && columndata.isSQLCompatible();
}
return result;
}
}
private class ColumnData {
private String columnName;
private String columnClassName;
private String columnTypeName;
private int columnSize;
private int columnScale; // Nachkommastellen
private int columnIndex;
private int columnType;
public String getColumnName() {
return this.columnName;
}
public boolean isSQLCompatible() {
// Ausschließen, dass SQL-Schlüsselworte als Feldnamen verwendet
// werden
// erstmal nur WHERE ausgeschlossen
boolean result = true;
if (this.columnName.toUpperCase().equals("WHERE")) {
result = false;
}
return result;
}
public void setColumnName(final String columnName) {
this.columnName = columnName;
}
public void setColumnClassName(final String columnClassName) {
this.columnClassName = columnClassName;
}
public void setColumnSize(final int columnSize) {
this.columnSize = columnSize;
}
public void setColumnType(final int columnType) {
this.columnType = columnType;
}
public void setColumnScale(final int columnScale) {
this.columnScale = columnScale;
}
public String getColumnSQLDefinition() {
String result;
if (this.columnName.toUpperCase().equals("WHERE")) {
result = "WHEREWHERE ";
} else {
result = this.columnName + " ";
}
if (this.columnClassName == "java.lang.String") {
if (this.columnSize > JdbcMoverSimple.this.MAXVARCHAR) {
result += "VARBINARY(MAX)";
} else {
result += "VARCHAR(" + this.columnSize + ")";
}
} else if (this.columnClassName == "java.lang.Integer") {
result += "INTEGER";
} else if (this.columnClassName == "java.math.BigDecimal") {
result += "DECIMAL(" + this.columnSize + ", "
+ this.columnScale + ")";
} else if (this.columnClassName == "java.sql.Date") {
result += "DATETIME";
} else if (this.columnClassName == "java.lang.Boolean") {
result += "BIT";
} else {
// Wir verwenden den Original-Spaltentyp
result += this.columnTypeName;
}
return result;
}
public void setColumnTypeName(final String columnTypeName) {
this.columnTypeName = columnTypeName;
}
/**
* Ein vorbereitetes Statement mit dem Wert für diese Spalte belegen
*
* @param targetstmt
* @param res
* @param useTempindex
*/
public void setStatementValue(final PreparedStatement targetstmt,
final ResultSet res) {
final int index = this.columnIndex;
try {
final Object value = res.getObject(this.getColumnName());
// System.out.println(" " + this.getColumnName() +
// " Value: "
// + ((null == value) ? "null" : value.toString()));
if (null == value) {
targetstmt.setNull(index, this.columnType);
} else {
targetstmt.setObject(index, value);
}
} catch (final Exception e) {
throw new ProcessException(
"Fehler bei der Zuweisung von Daten in Feld "
+ this.columnName + " (" + this.columnClassName
+ ")", e);
}
}
public void setColumnIndex(final int columnIndex) {
this.columnIndex = columnIndex;
}
public int getColumnIndex() {
return this.columnIndex;
}
public boolean isBlob() {
return (this.columnType == Types.LONGVARBINARY);
}
/**
* Ein vorbereitetes Statement mit einem InputStream für ein Blob
* versehen und füllen
*
* @param targetstmt
* @param workdir
*/
public void writeBlob(final PreparedStatement targetstmt,
final String workdir) {
final int index = this.columnIndex;
try {
final File blobfile = new File(this.getFileName(workdir));
final FileInputStream fis = new FileInputStream(blobfile);
targetstmt.setBinaryStream(index, fis, (int) blobfile.length());
} catch (final Exception e) {
throw new ProcessException(
"Fehler bei der Zuweisung von Blob-Daten in Feld "
+ this.columnName + " (" + this.columnClassName
+ ")", e);
}
}
/**
* Ein Blob aus dem Resultset in ein File auslesen
*
* @param res
* @param workdir
*/
public void readBlob(final ResultSet res, final String workdir) {
final String fileName = this.getFileName(workdir);
int count = 0;
try {
final InputStream is = res.getBinaryStream(this
.getColumnIndex());
final File blobfile = new File(fileName);
final FileOutputStream fos = new FileOutputStream(blobfile);
if (null == is) {
// Leere Datei schreiben
fos.write(0);
} else {
final byte[] buffer = new byte[4096];
// Get the binary stream of our BLOB data
while (is.read(buffer) > 0) {
fos.write(buffer);
count++;
System.out.println(" schreibe Puffer: " + count);
}
}
fos.close();
} catch (final Exception e) {
throw new ProcessException(
"Fehler bei der Zuweisung von Blob-Daten in Feld "
+ this.columnName + " (" + this.columnClassName
+ ")", e);
}
}
private String getFileName(final String workdir) {
String result = workdir;
if (result.endsWith("/")) {
} else {
result += "/";
}
result += "tempblob" + this.columnIndex + ".bin";
return result;
}
}
/**
* {@inheritDoc}
*/
public void setSourceConnection(final Connection conn) {
this.sourceconnection = conn;
this.sourcetablesAnalyzed = false;
}
/**
* {@inheritDoc}
*/
public Connection getSourceConnection() {
return this.sourceconnection;
}
/**
* {@inheritDoc}
*/
public void setDestinationConnection(final Connection conn) {
this.destinationconnection = conn;
}
/**
* {@inheritDoc}
*/
public Connection getDestinationConnection() {
return this.destinationconnection;
}
/**
* {@inheritDoc}
*/
public void move(final String tablename, final String workDirectory)
throws ProcessException {
if (!this.sourcetablesAnalyzed) {
this.analyzeDataStructure();
}
final TableData tabledata = this.findTabledataFor(tablename);
this.workdir = workDirectory;
if (null == tabledata) {
throw new ProcessException(
"Die Tabelle "
+ tablename
+ " existiert nicht in der Quelldatenbank oder ist nicht SQL-kompatibel.",
null);
} else {
if (this.createTable(tabledata)) {
this.copyData(tabledata);
}
}
}
private boolean createTable(final TableData tabledata)
throws ProcessException {
final Connection connection = this.getDestinationConnection();
String statement = null;
Statement stmt;
boolean result = false;
try {
stmt = connection.createStatement();
statement = "DROP TABLE " + tabledata.getTableName();
// System.out.println(" " + statement);
try {
stmt.execute(statement);
} catch (final Exception e) {
// Drop table darf schiefgehen
}
statement = "CREATE TABLE " + tabledata.getTableName() + " (";
for (final ColumnData columndata : tabledata.getTableColumns()) {
statement += columndata.getColumnSQLDefinition() + ", ";
}
// Letztes Komma wieder weg
statement = statement.substring(0, statement.length() - 2);
statement += ")";
// System.out.println(" " + statement);
stmt.execute(statement);
stmt.close();
result = true;
} catch (final SQLException e) {
throw new ProcessException(e.getMessage()
+ ((null == statement) ? "" : " Statement: " + statement),
e);
}
return result;
}
private void copyData(final TableData tabledata) throws ProcessException {
final Connection targetconnection = this.getDestinationConnection();
String sourcestatement = null, targetstatement = null;
Statement sourcestmt;
PreparedStatement targetstmt;
try {
final Connection sourceconnection = this.getSourceConnection();
sourcestmt = sourceconnection.createStatement();
// Statements zum Lesen und Schreiben zusammenbauen
sourcestatement = "SELECT ";
targetstatement = "INSERT INTO " + tabledata.getTableName() + " (";
for (final ColumnData columndata : tabledata.getTableColumns()) {
sourcestatement += columndata.getColumnName() + ", ";
targetstatement += columndata.getColumnName() + ", ";
}
// Letztes Komma wieder weg
sourcestatement = sourcestatement.substring(0,
sourcestatement.length() - 2)
+ " FROM " + tabledata.getTableName();
targetstatement = targetstatement.substring(0,
targetstatement.length() - 2)
+ ") VALUES (";
for (@SuppressWarnings("unused")
final ColumnData columndata : tabledata.getTableColumns()) {
targetstatement += "?, ";
}
// Letztes Komma entfernen
targetstatement = targetstatement.substring(0,
targetstatement.length() - 2)
+ ")";
targetstmt = targetconnection.prepareStatement(targetstatement);
// Daten aus der Quelltabelle lesen
// System.out.println(" " + sourcestatement);
final ResultSet res = sourcestmt.executeQuery(sourcestatement);
while (res.next()) {
// Erstmal die einfachen Datentypen übertragen
for (final ColumnData columndata : tabledata.getTableColumns()) {
if (columndata.isBlob()) {
} else {
columndata.setStatementValue(targetstmt, res);
}
}
// Nun die Blobs auslesen
for (final ColumnData columndata : tabledata.getTableColumns()) {
if (columndata.isBlob()) {
columndata.readBlob(res, this.workdir);
}
}
// und die Blobs schreiben
for (final ColumnData columndata : tabledata.getTableColumns()) {
if (columndata.isBlob()) {
columndata.writeBlob(targetstmt, this.workdir);
}
}
targetstmt.executeUpdate();
}
sourcestmt.close();
targetstmt.close();
} catch (final Exception e) {
throw new ProcessException(e.getMessage()
+ ((null == sourcestatement) ? "" : " Source-Statement: "
+ sourcestatement)
+ ((null == targetstatement) ? "" : " Insert-Statement: "
+ targetstatement), e);
}
}
public List<TableData> getTables() {
return this.tables;
}
private TableData findTabledataFor(final String tablename) {
TableData tabledata = null;
for (final TableData td : this.tables) {
if (td.getTableName().equals(tablename)) {
tabledata = td;
}
}
return tabledata;
}
/**
* Analysiert die gesamte Datenstruktur der Quelldatenbank und merkt sie
* sich.
*/
public void analyzeDataStructure() throws ProcessException {
try {
final Connection connection = this.getSourceConnection();
final DatabaseMetaData meta = connection.getMetaData();
final ResultSet res = meta.getTables(null, null, null,
new String[] { "TABLE" });
this.tables.clear();
TableData tabledata;
ColumnData columndata;
ResultSet resSelect;
Statement stmSelect;
ResultSetMetaData rsmdSelect;
int numColumnsSelect;
String tableName;
String statement;
// System.out.println("List of tables: ");
while (res.next()) {
tableName = res.getString("TABLE_NAME");
// System.out.println(" " + ", " +
// res.getString("table_owner")
// + ", " + tableName + ", " + res.getString("TABLE_TYPE")
// + ", " + res.getString("REMARKS"));
tabledata = new TableData();
tabledata.setTableName(tableName);
// Tabellenstruktur ermitteln
try {
stmSelect = connection.createStatement();
statement = "SELECT * FROM " + tableName;
resSelect = stmSelect.executeQuery(statement);
rsmdSelect = resSelect.getMetaData();
numColumnsSelect = rsmdSelect.getColumnCount();
for (int i = 1; i <= numColumnsSelect; i++) {
columndata = new ColumnData();
columndata.setColumnName(rsmdSelect.getColumnName(i));
columndata.setColumnClassName(rsmdSelect
.getColumnClassName(i));
columndata.setColumnTypeName(rsmdSelect
.getColumnTypeName(i));
columndata.setColumnType(rsmdSelect.getColumnType(i));
columndata.setColumnSize(rsmdSelect
.getColumnDisplaySize(i));
columndata.setColumnScale(rsmdSelect.getScale(i));
columndata.setColumnIndex(i);
tabledata.getTableColumns().add(columndata);
// System.out.println(" " +
// rsmdSelect.getColumnName(i)
// + " Typ: " + rsmdSelect.getColumnType(i) + " ("
// + rsmdSelect.getColumnTypeName(i) + ")"
// + " Klasse: "
// + rsmdSelect.getColumnClassName(i)
// + " DisplaySize: "
// + rsmdSelect.getColumnDisplaySize(i)
// + " Scale: " + rsmdSelect.getScale(i)
// + " Label: " + rsmdSelect.getColumnLabel(i));
}
stmSelect.close();
// Sonderfall: Feldname "where" muss ausgeschlossen werden,
// Tabelle wird ignoriert
if (tabledata.isSQLCompatible()) {
this.tables.add(tabledata);
}
} catch (final Exception e) {
// Tabelle kann nicht gelesen werden, wahrscheinlich leer.
throw new ProcessException(
"Fehler beim Ermitteln der Datenstrukturen in der Quelldatenbank: "
+ e.getMessage(), e);
}
}
res.close();
this.sourcetablesAnalyzed = true;
} catch (final Exception e) {
throw new ProcessException(
"Fehler beim Ermitteln der Datenstrukturen in der Quelldatenbank: "
+ e.getMessage(), e);
}
}
}