Advanced   Java   Services PreparedStatement und Blob in MySQL Back Next Up Home


Blob

Eine interessante Anwendung für PreparedStatements ist der Datentyp Blob. Blob (binary large object) gehört nicht zu den klassischen SQL-Datentypen und wurde erst später als SQL-Datentyp eingeführt. Java unterstützt Blobs seit JDBC 2.0 . In einer Spalte vom Typ Blob kann man beliebige Binärdaten ablegen. Im Unterschied zu den klassischen Datentypen kann man aber von Java aus den Typ Blob nicht über ein normales Statement setzen. Man kann mit executeUpdate(String sql) einen String wie "insert into coffees values('Aldi spezial', 69, 3.33, 44, 55)" abschicken, aber den Wert eines Blobs kann man sicher nicht in einem insert unterbringen. Hier hilft jedoch ein PreparedStatement weiter. Zunächst jedoch wollen wir uns eine einfache Tabelle mit einer Spalte vom Typ Blob anlegen. Vielleicht wollen wir unsere Urlaubsphotos in einer Tabelle speichern.




Tabelle mit einer Blob-Spalte anlegen

Wir legen eine Tabelle an mit drei Spalten. Die erste Spalte dient für eine Beschreibung des Bildes, die zweite Spalte soll das Bildformat (".jpg", ".gif", ".png" etc.) enthalten und die dritte Spalte schließlich die Binärdaten des Bildes im Datentyp Blob. Das kann folgendermaßen aussehen.

Statement stmt = connection.createStatement();
String createString = "create table urlaub2001(description VARCHAR(255), imagetype VARCHAR(8), image BLOB)";
stmt.executeUpdate(createString);

Für eine Datenbank ist allerdings Blob nicht gleich Blob. So gibt es etwa in MySQL vier verschiedene Blobtypen. Von Java aus gesehen gibt es jedoch nur einen Typ Blob, mit welchem speziellen BlobTyp die Tabelle angelegt worden ist spielt keine Rolle. Hier ein Auszug aus dem Manual von MySQL, der die verschiedenen Blobvarianten darstellt.


Aus dem MySQL manual

Hier ein Auszug aus dem Manual von MySQL, der die verschiedenen Blobvarianten darstellt.

Blob (und Text) Varianten in MySQL
TypMaximale Größe
TINYBLOBTINYTEXT 2^8-1 (255)
BLOBTEXT 2^16-1 (65535)
MEDIUMBLOBMEDIUMTEXT 2^24-1 (16777215)
LONGBLOBLONGTEXT 2^32-1 (4294967295)

A BLOB is a binary large object that can hold a variable amount of data. The four BLOB types TINYBLOB, BLOB, MEDIUMBLOB, and LONGBLOB differ only in the maximum length of the values they can hold.

The four TEXT types TINYTEXT, TEXT, MEDIUMTEXT, and LONGTEXT correspond to the four BLOB types and have the same maximum lengths and storage requirements. The only difference between BLOB and TEXT types is that sorting and comparison is performed in case-sensitive fashion for BLOB values and case-insensitive fashion for TEXT values. In other words, a TEXT is a case-insensitive BLOB.

If you assign a value to a BLOB or TEXT column that exceeds the column type's maximum length, the value is truncated to fit.

In most respects, you can regard a TEXT column as a VARCHAR column that can be as big as you like. Similarly, you can regard a BLOB column as a VARCHAR BINARY column.

Note that each BLOB or TEXT value is represented internally by a separately allocated object. This is in contrast to all other column types, for which storage is allocated once per column when the table is opened.


Anlegen der Tabelle

Der Typ MEDIUMBLOB dürfte wohl für die meisten Urlaubphotos ausreichend sein. Wir legen also unsere Tabelle folgendermaßen an:

Statement stmt = connection.createStatement();
String createString = "create table URLAUBS_PHOTOS_2001(description VARCHAR(255)," +
                      " imagetype VARCHAR(8), image MEDIUMBLOB)";
stmt.executeUpdate(createString);

Wer eine größere Flexibilität haben will, packt das ganze in eine Methode. Etwa so :

public boolean createPhotoTable(Connection conn, String tableName, String blobType)
{
   try
   {
      Statement stmt = conn.createStatement();
      String createString = "create table " + tableName +
                            " (name varchar(32), image " + blobType + ")" ;
      stmt.executeUpdate(createString);
      System.out.println("Neue Tabelle " + tableName + " erstellt");
      stmt.close();
      return true;
   }
   catch(SQLException ex)
   {
      System.out.println("IOException = " + ex);
      return false;
   }
}

Als nächstes wollen wir unsere Tabelle mit Daten füttern.


Binärdaten in die Tabelle einfügen



Hier hilft uns ein PreparedStatement. Zunächst legen wir einen Einfügestring mit Fragezeichen an.

String pSql ="insert into URLAUBS_PHOTOS_2001 values(?,?,?)";
PreparedStatement pStmt = conn.prepareStatement(pSql);

Nun liefern wir die Werte nach und schicken das ganze an die Datenbank ab. Das Interface PreparedStatement bietet dazu für den Datentyp BLOB die folgenden zwei Methoden an.

PreparedStatement
voidsetBinaryStream(int parameterIndex, InputStream x, int length)
Sets the designated parameter to the given input stream, which will have the specified number of bytes.
voidsetBlob(int i, Blob x)
Sets the designated parameter to the given Blob object.

Die zweite Methode schaut einfacher aus, setzt jedoch ein existierndes BlobObjekt voraus. Blob ist allerdings ein Interface, wir können kein BlobObjekt erzeugen, wir können nur (später) ein in der Tabelle vorhandes BlobObjekt auslesen. Wir verwenden deshalb die erste Methode.

pStmt.setString( 1, "In Spanien (Ich und der Stier)");  // ersten Parameter setzen
pStmt.setString( 2, ".jpg");  // zweiten Parameter setzen

// der Methode setBinaryStream() die Bilddaten von der Platte geben
InputStream photoStream = new BufferedInputStream( new FileInputStream(pathToImage));
pStmt.setBinaryStream( 3, photoStream, photoStream.available() );

// Statement abschicken
pStmt.execute();

Auslesen der Binärdaten aus der Tabelle



Natürlich wollen wir unsere Daten auch wieder aus der Tabelle auslesen und suchen die Pendants zu setBinaryStream() und setBlob(). Hier werden wir im Interface ResultSet fündig.

ResultSet
InputStreamgetBinaryStream(int columnIndex)
Retrieves the value of the designated column in the current row of this ResultSet object as a binary stream of uninterpreted bytes.
InputStreamgetBinaryStream(String columnName)
Retrieves the value of the designated column in the current row of this ResultSet object as a binary stream of uninterpreted bytes.
BlobgetBlob(int i)
Retrieves the value of the designated column in the current row of this ResultSet object as a Blob object in the Java programming language.
BlobgetBlob(String colName)
Retrieves the value of the designated column in the current row of this ResultSet object as a Blob object in the Java programming language.

Wir können also folgendermaßen vorgehen:

String query = "select * from URLAUBS_PHOTOS_2001" ;
ResultSet rs = stmt.executeQuery(query);
rs.next();  // jetzt sind wir in der ersten Zeile

//Auslesen der ersten Zeile
String photName = rs.getString(1);
String type =  rs.getString(2);

// Blob auslesen
BufferedInputStream bis = new BufferedInputStream( rs.getBinaryStream(3) );
BufferedImage bufImage = ImageIO.read(bis)  ;
Mit der Klasse ImageIO, die neu in 1.4 ist, kann man aus dem InpuStream im Handumdrehen ein
BufferedImage erzeugen, das man dann etwa auf ein JLabel legen kann und in einem JScrollPane
präsentieren kann.
JLabel imageLabel = new JLabel( new ImageIcon(bufImage) );
JScrollPane jsp = new JScrollPane(imageLabel);
// etc.

Wer will, kann beim Einlesen auch mit einem BlobObjekt arbeiten. Die Variante unterscheidet sich jedoch nicht wirklich von der vorhergehenden.

// Blob auslesen mit Blob
Blob blubb = rs.getBlob(3);
BufferedInputStream bis = new BufferedInputStream( blubb.getBinaryStream() );
BufferedImage bufImage = ImageIO.read(bis)  ;

Das Vorgehen für etwa Oracle unterscheidet sich nicht grundsätzlich von dem hier beschriebenen Vorgehen. Varianten kann es nur beim Anlegen der Tabelle geben. Das Auslesen der Daten erfolgt genauso.





Valid XHTML 1.0 Strict top Back Next Up Home