Es kommt immer wieder vor, dass man keine speziellen ORM-Frameworks für den Zugriff auf Datenbank nutzt oder nutzen kann und zum direkten JDBC-Zugriff zurückgreifen muss oder will. Wie werden die entsprechenden SQL-Statements im Code hinterlegt? Meistens als Konstanten, manchmal wird das Statement auch dynamisch aufgebaut. Was mich immer wieder gestört hat bei der Variante mit den Konstanten, ist die Lesbarkeit der SQL-Statements innerhalb des Codes. Werden die SQL-Statements umfangreicher, so ist es kaum noch nachzuvollziehen, was innerhalb des Statements passiert….wenn Einrückungen und Klammerungen dann auch noch vernachlässigt werden ist es ganz aus. Daher habe ich mir jetzt eine Variante ausgedacht, die eine Lösung darstellt: auslagern der SQL-Statements in Textdateien, die innerhalb der Packages liegen und zur Laufzeit eingelesen werden.
Die SQL-Statements sollen innerhalb der Klassen als “Konstanten” abgelegt sein, dazu werden sie zur Laufzeit beim Laden der Klasse durch den Classloader initialisiert. Hierzu wird statischer Code verwendet. Eine entsprechende Utitlityklasse, die sich um das Einlesen der Textdateien kümmert, wird hier als Code aufgeführt, ebenso Beispielcode, wie es genutzt werden kann.
package de.maicoda.utilities;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import org.apache.log4j.Logger;
public class ResourceReader
{
static Logger log = Logger.getLogger( ResourceReader.class );
/**
* Liest aus dem Classpath eine Resource ein und liefert den Inhalt als String zurück.
* Besonders gedacht zum Einlesen von Textdateien, die innerhalb von Packages liegen.
*
* @param resource Pfad und Name der Resource zusammen, z.B. de/sinform/dwh/Test.sql
* @return Inhalt der Resource als Text
*/
public static String readResourceAsString( String resource )
{
ClassLoader classLoader = ResourceReader.class.getClassLoader();
LineNumberReader reader = new LineNumberReader( new InputStreamReader( classLoader.getResourceAsStream( resource ) ) );
String zeile = null;
StringBuffer puffer = new StringBuffer();
try
{
if( reader.ready() )
{
while( ( zeile = reader.readLine() ) != null )
{
puffer.append( zeile );
puffer.append( "\n" );
}
}
}
catch( IOException e )
{
log.error( e );
}
finally
{
if( reader != null )
{
try
{
reader.close();
}
catch( IOException e1 )
{
log.error( "Fehler beim Schliessen des Readers für die Datei " + resource, e1 );
}
}
}
return puffer.toString();
}
/**
* Liest aus dem Classpath eine Resource ein und liefert den Inhalt als String zurück.
* Besonders gedacht zum Einlesen von Textdateien, die innerhalb von Packages liegen.
*
* @param pfad Pfad (Package), in dem die Resource liegt, z.B. de/sinform/dwh/
* @param resource Name der Resource, z.B. Test.sql
* @return Inhalt der Resource als Text
*/
public static String readResourceAsString( String pfad, String resource )
{
if( pfad.endsWith( "/" ) == false )
{
pfad += "/";
}
return readResourceAsString( pfad + resource );
}
/**
* Liest aus dem Classpath eine Resource ein und liefert den Inhalt als String zurück.
* Besonders gedacht zum Einlesen von Textdateien, die innerhalb von Packages liegen.
*
* @param packagename Referenz auf das Package der Resource
* @param resource Name der Resource, z.B. Test.sql
* @return Inhalt der Resource als Text
*/
public static String readResourceAsString( Package packagename, String resource )
{
String pfad = packagename.getName().replaceAll( "\\.", "/" ) + "/";
return readResourceAsString( pfad, resource );
}
}
Diese Utilitieklasse kann nun wie folgt genutzt werden. Dabei wird davon angenommen, dass die die Textdatei mit dem SQL-Code (NeuerKunde.sql) im gleichen Package liegt wie die Klasse SqlKunde.
private static final String sqlNeuerKunde = ResourceReader.readResourceAsString( SqlKunde.class.getPackage(), "NeuerKunde.sql" );
Das Ergebnis ist nun eine initialisierte Konstante sqlNeuerKunde, in der das entsprechende SQL-Statement zu finden ist. Die Initialisierung findet beim Laden der Klasse statt.