Abstraktion von Datenbankoperationen mit Spring und Hibernate erhöht die Wartbarkeit und vereinfacht die weitere Codeerstellung

CRUD mit Spring 3.2 und Hibernate 4.2

Abstraktion von Datenbankoperationen mit Spring und Hibernate erhöht die Wartbarkeit und vereinfacht die weitere Codeerstellung

Die Java-Frameworks Spring und Hibernate werden bei der Applikationsentwicklung oft gemeinsam verwendet. Spring bietet mittlerweile zwar eine gute Hibernate-Anbindung, das Zusammenspiel ist aber nicht immer einfach und klar; nicht jede Version von Spring funktioniert mit jeder Version von Hibernate. Dennoch bietet die Kombination der beiden Frameworks interessante Optionen. In diesem Beitrag zeigen wir eine neue Möglichkeit, den Datenbankzugriff zu abstrahieren, wozu wir Spring 3.2 und Hibernate 4.2 kombiniert haben.

Datenbankzugriffe werden in Java über die Datenbankschnittstelle JDBC (Java Database Connectivity) durchgeführt. Diese Methode ist einfach, jedoch fällt eine Abstraktion beziehungsweise das Zusammenfassen von ähnlichen Methoden schwer. Auch muss die Verbindung zur Datenbank manuell verwaltet werden.

Eine einfachere Methode bietet Spring mit dem JDBC-Template und der automatischen Verwaltung der Verbindung zur Datenbank. Das Framework kümmert sich selbstständig um die Verbindung zur Datenbank, man muss nur die SQL-Statements selber schreiben. Spring bringt außerdem noch eine Verwaltung von Beans und damit auch Dependency-Injection mit.

Die Verwendung von Hibernate führt eine weitere Abstraktionsebene ein, welche ein Mapping von Modell-Klassen direkt auf Tabellen erlaubt. Ein weiterer Effekt dieses Mappings ist, dass Datenbank-Constraints auf Modell-Klassen abgebildet werden können, dadurch ist eine Prüfung der Constraints zur Laufzeit, noch vor eines Commits zur Datenbank, möglich.

Durch Verwendung von Generics, Interfaces und abstrakten Klassen im Zusammenspiel mit Spring und Hibernate lassen sich CRUD-Zugriffe auf die Datenbank sehr gut abstrahieren, sodass nur eine allgemeine Klasse diese erledigt und lediglich spezielle Aggregationen eindeutig zu implementieren sind.

Im folgenden Beispiel soll Spring die Verwaltung der Datenzugriffsschicht übernehmen und Hibernate den eigentlichen Datenzugriff. Für Hibernate wird ein Primary Key benötigt, um Tupel eindeutig identifizieren zu können. Dieser Key wird im folgenden Beispiel als ID in den Klassen abgebildet. Diese ID wird in der Basis-Modell-Klasse über Typ-Parameter für alle weiteren Modell-Klassen definiert
public abstract class AbstractEntityObject<T extends Serializable>
Die Modell-Klasse Employee muss diese ID nicht mehr implementieren, sondern nur noch AbstractEntityObject erweitern.
public class Employee extends AbstractEntityObject<Long>
Mit der Abstraktion der ID ergibt sich, dass auch ein Basis-DAO erstellt werden kann.
public interface InterfaceBasicDaoSupport<E extends AbstractEntityObject<I>, I extends Serializable> {
  public E findById(I id);
  public void update(E e);
  public List<E> findAll();
  public void saveOrUpdate(E e);
  public void delete(E e);
  public void merge(E e);
  public List<E> findByCriteria(Criterion criterion);
}

public abstract class AbstractBasicDaoSupport<E extends AbstractEntityObject<I>, I extends Serializable> implements InterfaceBasicDaoSupport<E, I> {
  protected Class<E> entityClass;
  protected AbstractBasicDaoSupport(Class<E> entityClass) {
    this.entityClass = entityClass;
  }
  public E findById(I id) {...}
  public void update(E e) {...}
  public List<E> findAll() {...}
  public void saveOrUpdate(E e) {...}
  public void delete(E e) {...}
  public void merge(E e) {...}
  public List<E> findByCriteria(Criterion criterion) {...}
}

Es werden auch alle CRUD-Methoden im AbstractBasicDaoSupport definiert. Dadurch ergibt sich ein fast leere Implementierung für den Zugriff auf Employee.
public interface InterfaceEmployeeDao extends InterfaceBasicDaoSupport<Employee, Long> {
}

public class EmployeeDao extends AbstractBasicDaoSupport<Employee, Long> implements InterfaceEmployeeDao {
  public EmployeeDao() {
    super(Employee.class);
  }
}

Folglich kann auch ein Basis-Service erstellt werden.
public interface InterfaceBasicService<E extends AbstractEntityObject<I>, I extends Serializable, D extends InterfaceBasicDaoSupport<E, I>> {
  public E findById(I id);
  public void update(E e);
  public List<E> findAll();
  public void save(E e);
  public void delete(E e);
}

public abstract class AbstractBasicService<E extends AbstractEntityObject<I>, I extends Serializable, D extends InterfaceBasicDaoSupport<E, I>> {
  public E findById(I id) {...}
  public void update(E e) {...}
  public List<E> findAll() {...}
  public void save(E e) {...}
  public void delete(E e) {...}
}

Das Service für Employee wird durch die Abstraktion auch zu einer sehr kleinen Klasse.
public interface InterfaceEmployeeService extends InterfaceBasicService<Employee, Long, InterfaceEmployeeDao> {
}

public class EmployeeService extends AbstractBasicService<Employee, Long, InterfaceEmployeeDao>{
}

Im DAO und Service müssen nur noch spezielle Methoden implementiert werden, die CRUD-Funktionalität ist schon durch AbstractBasicDaoSupport und AbstractBasicService abgedeckt.

Durch die Kombination von Spring und Hibernate ist der Code besser wartbar, und man spart sich bei der Code-Erstellung Zeit, da die CRUD-Funktionen nur einmal definiert werden. Man sollte jedoch beachten, dass im Fall eines Fehlers bei der Implementierung die Fehlersuche doch um einiges schwieriger ist.

Das komplette Beispiel findet man bei GitHub unter:
https://github.com/Splendit/hibernate-example