JDBC - Java Database Connectivity

Die Java JDBC API definiert eine Vielzahl von Methoden, über die auf alle gängigen Datenbanken zugegriffen werden kann. Sie stellt eine transparente Zwischenschicht dar, um Inhalte von Datenbanken verschiedener Hersteller mit einheitlichen Aufrufen zu lesen und zu schreiben. Somit ist nicht nur ein schneller und unkomplizierter Wechsel von einer Datenbank zu einer anderen möglich, sondern es können auch Softwaresysteme entwickelt werden, die durch geschickte Parametrisierung den Zugriff auf verschiedene Datenbanksysteme erlauben.

Voraussetzungen

Zwar wird mit der JDBC API eine einheitliche Schnittstelle angeboten, jedoch werden die spezifischen Klassen jeweils von den Datenbankanbietern realisiert. Deshalb müssen diese Klassen, die in der Regel als .jar Dateien zur Verfügung stehen, zunächst einmal beschafft und dann zur Laufzeit über den Java CLASSPATH eingebunden werden.

Database Connections

Für jeden Zugriff auf eine Datenbank wird zunächst einmal eine Connection benötigt und an diese Connection gelangt man in der Regel über die Registrierung eines Treibers seiner datenbankspezifischen URL, einem User-Namen und dem zugehörigen Passwort:

import java.sql.*;

public class JdbcConExample
{ public static void main(String[] args) throws Exception
  { Class.forName("oracle.jdbc.driver.OracleDriver");
    String dbUrl = "jdbc:oracle:thin:@myHostName:1521:myDatabaseName";
    Connection con = DriverManager.getConnection(dbUrl, "scott", "tiger");
    try
    { // Exec Database calls ...
    }
    finally
    { con.close();
    }
  }
}
Vendor Driver-Class / Database-URL Driver-Lib
DB2 COM.ibm.db2.jdbc.net.DB2Driver
jdbc:db2://{host}[:{port}]/{dbname}
db2java.zip
Derby org.apache.derby.jdbc.EmbeddedDriver
bzw. org.apache.derby.jdbc.ClientDriver
jdbc:derby://server[:port]/databaseName[;URLAttributes=value[;...]]
derby.jar
HSQLDB org.hsqldb.jdbcDriver
jdbc:hsqldb[:{dbname}][:hsql://{host}[/{port}]]
hsqldb.jar
MS SQL-Server com.microsoft.jdbc.sqlserver.SQLServerDriver
jdbc:microsoft:sqlserver://{host}[:{port}][;DatabaseName={dbname}]
msbase.jar
mssqlserver.jar
msutil.jar
MySQL com.mysql.jdbc.Driver
jdbc:mysql://{host}[:{port}]/[{dbname}]
mysql-connector-java-5.1.5-bin.jar
Oracle oracle.jdbc.driver.OracleDriver
jdbc:oracle:thin:@{host}:{port}:{dbname}
classes12.zip
Postgre org.postgresql.Driver
jdbc:postgresql://[{host}[:{port}]]/{dbname}
pgdev.307.jdbc3.jar

Zufriffs Templates

Der Zugriff auf die Datenbanken folgt im Wesentlichen immer nach dem gleichen Schema. Wichtig in diesem Zusammenhang ist lediglich, peinlich darauf zu achten, dass geöffnete Ressourcen wie ResultSets und PreparedStatements nach ihrer Verwendung auf jeden Fall wieder geschlossen werden, denn sonst kann es zu unangenehmen Überraschungen wie beispielsweise Out-Of-Memory Fehlern kommen. Wichtig ist weiterhin, String-Werte in Select-Statements mit Hochkommas wie hier bei 'StringValue' zu begrenzen:

void selectTemplate() throws Exception
{ ResultSet rs = null;
  PreparedStatement ps = null;
  try
  { ps = con.prepareStatement("SELECT * from MyDB");
    rs = ps.executeQuery();
    while (rs.next())
    { String content = rs.getString("fieldName");
      // ...
    }
  }
  finally
  { if (rs  != null) 
      try { rs.close();  } 
      catch (Exception ex) { /* ex.printStackTrace(); */ }
    if (ps  != null) 
      try { ps.close();  } 
      catch (Exception ex) { /* ex.printStackTrace(); */ }
  }
}
int insertTemplate() throws Exception
{ PreparedStatement ps = null;
  try
  { Sting stmt =
    "INSERT INTO MyDB (Field_1,Field_2,Field_n) VALUES (1,'string_val_2',4711)"
    ps = con.prepareStatement(stmt);
    return ps.executeUpdate(); // num rows affected
  }
  finally
  { if (ps  != null) 
      try { ps.close();  } 
      catch (Exception ex) { /* ex.printStackTrace(); */ }
  }
}
int updateTemplate() throws Exception
{ PreparedStatement ps = null;
  try
  { Sting stmt = 
      "UPDATE MyDB SET Field_1 = 1, Field_2 = 'string_val_2', Field_n = 4711 "
    + "WHERE Index_Field = 'Index-Value'"
    ps = con.prepareStatement(stmt);
    return ps.executeUpdate(); // num rows affected
  }
  finally
  { if (ps  != null) 
      try { ps.close();  } 
      catch (Exception ex) { /* ex.printStackTrace(); */ }
  }
}
int deleteTemplate() throws Exception
{ PreparedStatement ps = null;
  try
  { ps = con.prepareStatement("DELETE FROM MyDB WHERE Field_Name = 'Field-Value'");
    return ps.executeUpdate(); // num rows affected
  }
  finally
  { if (ps  != null) 
      try { ps.close();  } 
      catch (Exception ex) { /* ex.printStackTrace(); */ }
  }
}

Transaktions Verarbeitung

JDBC Datenbanken befinden sich nach dem Öffnen im AutoCommit-Modus, d.h. jeder Update, Insert und Delete wirkt sich unmittelbar auf den Inhalt der Datenbank aus. Wenn gefordert ist, dass ein kompletter Satz von Datenbank-Anweisungen entweder durchgeführt, oder im Fehlerfall die Datenbank in den ursprünglichen Stand zurück versetzt werden soll, müssen diese Anweisungen durch eine Transaktion geklammert werden.

void myTransaction(Connection con) throws Exception
{ try
  { // 1. Einleitung der Transaktion
    con.setAutoCommit(false);

    // 2. Ausführung der Transaktion
    myDatabaseAccess_1();
    myDatabaseAccess_2();
    // ...
    myDatabaseAccess_N();

    // 3. Abschluß der Transaktion
    con.commit();
  }
  catch (Exception ex)
  {  // Zurückfahren der Transaktion im Fehlerfall
     con.rollback();
     throw ex;
  }
  finally
  { // Commit-Modos in jedem Fall wieder auf Auto setzen!
    con.setAutoCommit(true);
  }
}

Voraussetzung ist natürlich, dass die Datenbank Transaktionen unterstützt. Tut sie das nicht, arbeitet sie in der Regel immer im Modus AutoCommit, ganz egal, ob man sie mittels setAutoCommit(...) auf true oder false einstellt und auch ein commit() oder rollback() bewirkt in diesem Fall nichts. Allerdings sauberer ist, zunächst einmal festzustellen, ob die Datenbank überhaupt Transaktionen unterstützt und den Programmcode dann daraufhin abzustimmen:

boolean supportsTransactions(Connection con) throws SQLException
{ return con.getMetaData().supportsTransactions();
}