A Match made in Heaven

Serviceabstraktion

Die Spring-Serviceabstraktion wird für verschiedene APIs wie JDBC, JMS oder Transaktionen angeboten. Die Abstraktionen bieten verschiedene Vorteile:

  • Es werden RuntimeExceptions genutzt, was die Verwendung der APIs einfacher und sicherer macht.
  • Verschiedene API-Konzepte werden vereinheitlicht. So gibt es in Java Transaktions-Handling in JDBC, Hibernate, JPA, JDO, JTA oder JMS – und jedes Mal ein wenig anders. Spring vereinheitlicht solche APIs.
  • APIs wie JMS, JTA oder JDBC machen das korrekte Schließen von Ressourcen sehr schwierig. Spring automatisiert es, sodass der Code einfacher und sicherer wird.

Als Beispiel für die Serviceabstraktion soll JDBC dienen. Die entscheidende Klasse ist das JdbcTemplate. Sie funktioniert eigentlich recht einfach mit Scala – abgesehen von der notwendigen Konvertierung wegen der unterschiedlichen Basisdatentypen (Listing 7).

Listing 7: Namenskonvention
class CustomerDAO(dataSource: DataSource) {
 val jdbcTemplate = new JdbcTemplate(dataSource)
 def deleteById(id: Int) =
   jdbcTemplate.update(
    "DELETE FROM CUSTOMER WHERE ID=?",
    id : java.lang.Integer)
}

Schwierig wird der Zugriff auf Ressourcen wie eine JDBC ResultSet. Diese muss von Spring verwaltet werden, weil sie am Ende der Nutzung geschlossen werden und durch Spring implementiert werden muss. Daher nutzt Spring in diesem Fall einen Ansatz mit Callbacks, wie im Java-Code in Listing 8 gezeigt. In der getByName-Methode muss aus jeder Zeile aus der Datenbank ein Kunde erzeugt werden. Dazu wird der CustomerResultSetRowMapper genutzt, der mit jeder Zeile aus dem Ergebnis aufgerufen wird und sie in einen Customer umwandelt.

Listing 8: Spring JDBC Callbacks mit Java
public class CustomerDAO extends SimpleJdbcDaoSupport {
  private static final class CustomerResultSetRowMapper
    implements ParameterizedRowMapper {
      public Customer mapRow(ResultSet rs, int rowNum) {
        Customer customer = new Customer(rs.getString(1),
          rs.getString(2), rs.getDouble(4));
        customer.setId(rs.getInt(3));
        return customer;
  }
  }
  public List getByName(String name) {
    return getSimpleJdbcTemplate()
      .query(
         "SELECT * FROM T_CUSTOMER WHERE NAME=?",
         new CustomerResultSetRowMapper(), name);
  }
}

Ende

Dieser Ansatz benötigt recht viel Code. Die Callbacks sind letztendlich Funktionen, die für jede Zeile aufgerufen werden. Scala bietet eine umfangreiche Unterstützung von Funktionen – können also Templates mit Scala-Funktionen genutzt werden? Das ist möglich, wie Listing 9 zeigt. Allerdings müssen dazu die Funktionen in Callback-Klassen wie den RowMapper umgewandelt werden. Das ist in Scala mithilfe von Implicits möglich. Dadurch wird die Nutzung von Templates wesentlich weniger aufwändig: Wenn ein RowMapper erwartet wird, kann einfach eine Funktion genutzt werden. Sie wird dann hinter den Kulissen zu einem RowMapper.

Allerdings gibt es in den APIs noch einige Detailprobleme. Während Scala Option[T] als Typ kennt, der Objekte kennzeichnet, die nicht gesetzt sein können, nutzt Spring als Java-Framework einfach null. Ebenso erwartet Spring an einigen Stellen ein Class-Objekt, das Scala mit classOf[T] anbietet – in reinem Scala-Code könnte stattdessen T genutzt werden. Letztendlich wäre also ein Wrapper um das Spring-Framework für Scala-Entwickler noch natürlicher, aber auch wesentlich aufwändiger zu implementieren.

Listing 9: Spring JDBC mit Scala Funktionen
def findById(id: Int): Option[Customer] = {
  val result: Buffer[Customer] =
   jdbcTemplate.query(
     "SELECT * FROM CUSTOMER C WHERE C.ID=?",
      (rs: ResultSet) => {
        Customer(rs.getInt(1), rs.getString(2),
         rs.getString(3), rs.getDouble(4))
      } : RowMapper[Customer]
      ,id : java.lang.Integer)
    result.headOption
}

Themen der foglenden Seiten:

  • Aspektorientierte Programmierung (AOP)
  • Transactional Funktion
  • Infos zum Autoren
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.