Verwendung von BLOBs mit Hibernate und PostgreSQL

0101101 No Comments »

Das Filesystem. Unendliche Weiten. Dies sind die Abenteuer des Paketes java.io… Ich steh nicht so auf Abenteuer beim produktiven Programmieren. Dateirechte, Pfade… um nur einige Probleme zu nennen. Daher wollte ich probieren, Bilder binär in die Datenbank zu schreiben. Daraus ist dieses kurze “HowTo” über das Speichern von binären Daten in PostgreSQL mit dem O/R Mapper Hibernate entstanden. Die Ausgabe erfolgt über ein Servlet und eine JSP.

Das Mapping

Nach einiger Recherche habe ich herausgefunden, dass sich der Postgres-Datentyp bytea am besten für diese Aufgabe eignet. Das ganze wird in Hibernate einfach wie folgt gemappt:

<property name="imageBlob" type="binary">
    <column name="image_blob" />
</property>

Die gemappte Klasse verwendet dann einfach nur den Datentyp byte[]. Inklusive Get/Set-Methoden sieht das dann so aus:

private byte[] imageBlob;

public byte[] getImageBlob() {
    return imageBlob;
}

public void setImageBlob(byte[] imageBlob) {
    this.imageBlob = imageBlob;
}

Das Servlet

ByteArray-Streams eignen sich gut zur Weiterverarbeitung. Als Beispiel dient das folgende Servlet, dass einfach das Image wieder ausgibt und eine hochgeladene Datei in die Datenbank schreibt. Für den Upload habe ich das Framework “commons-upload” von Apache.org verwendet.

Zuerst die Get-Methode zur Ausgabe des Bildes:

protected void doGet(
    HttpServletRequest request,
    HttpServletResponse response) {

    String type = request.getParameter("type");
    String uid = request.getParameter("uid");

    if (type == null) {
        response.sendError(404);
        return;
    }

    byte[] byteArray;
    if (type.equals("scene")) {
        // Get the mapped class
        GeScene scene = sceneService.get(Integer.parseInt(uid));
        byteArray = scene.getImageBlob();
    } else {
        response.sendError(404);
        return;
    }

    InputStream in = new ByteArrayInputStream(byteArray);
    OutputStream out = response.getOutputStream();

    int b;
    while ((b = in.read()) != -1) {
        out.write(b);
    }

    in.close();
    out.flush();
    out.close();
}

Und dann noch die POST Methode des Servlets:

protected void doPost(
    HttpServletRequest request,
    HttpServletResponse response) {
    // Alles zur FileItemFactory kann man aus der
    // Doku zu commons-upload erfahren.
    ServletFileUpload upload = new ServletFileUpload(
        newDiskFileItemFactory(getServletContext(), new File("tmp")));
    String uid = request.getParameter("uid");
    try {
        List<FileItem> files = upload.parseRequest(request);
        for (FileItem fileItem : files) {
            if (!fileItem.isFormField()) {
                GeScene scene = sceneService.get(Integer.parseInt(uid));
                scene.setImageBlob(fileItem.get());
                sceneService.update(scene);
            }
        }
    } catch (Exception e) {
        // Error Handler
    } finally {
        RequestDispatcher disp =
    request.getRequestDispatcher("imageservice.jsp");
        disp.forward(request, response);
    }
}

Das JSP

Fehlt noch das passende JSP für den Upload:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%
    String uid = request.getParameter("uid");
%>

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>

        <img src="ImageService?type=scene&amp;uid=<%=uid %>"
            width="100" height="100" />

        <form action="ImageService?type=scene&amp;uid=<%=uid %>"
            enctype="multipart/form-data" method="post">
            <input type="file" name="file">
            <button>Submit</button>
        </form>

    </body>
</html>

Fazit:

Das Mapping funktioniert denkbar einfach, auch die Verarbeitung artet keineswegs in Probleme aus. Nun wird sich im Laufe der Zeit zeigen, ob das ganze auch performant ist.

PostGIS und Hibernate

0101101 No Comments »

Für die Speicherung von Geo-Daten, verwenden wir das DBMS PostgreSQL mit der Erweiterung PostGIS. Die Datenbank wird von einem Frontend gefüttert und abgefragt, das Hibernate für das OR-Mapping verwendet. Da Hibernate den PostGIS Dialekt nicht spricht, haben wir auf den Dialekt von Norman Baker zurückgegriffen. Dazu haben wir die Klassen PostGISDialect und GeometryType aus dem PostGIS SVN (/trunk/java/ejb3/src/org/postgis/hibernate) in unser Projekt kopiert. Die Hibernate Config sieht nun so aus:

jdbc.driverClass=org.postgis.DriverWrapper
hibernate.dialect=org.postgis.myhibernate.PostGISDialect

Das Hibernate Mapping für eine Tabellenspalte mit Datentyp Geometry:

<property name="geom" type="org.postgis.myhibernate.GeometryType">
    <column name="geom" />
</property>

Und die Deklaration der Membervariable in der gemappten Klasse:

private org.postgis.Point geom;

Somit kann man nun PostGIS Datentypen in Java verwenden. Vielen Dank an Norman Baker.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Anmelden