Vereinfachen Sie das DAO mit Spring und Java Generics

1. Übersicht

Dieser Artikel konzentriert sich auf die Vereinfachung der DAO-Ebene durch Verwendung eines einzelnen generierten Datenzugriffsobjekts für alle Entitäten im System, was zu einem eleganten Datenzugriff ohne unnötige Unordnung oder Ausführlichkeit führt.

Wir werden auf der Abstract DAO-Klasse aufbauen, die wir in unserem vorherigen Artikel über Spring and Hibernate gesehen haben, und Generika-Unterstützung hinzufügen.

2. Die DAOs Hibernate und JPA

Die meisten Produktionscodebasen haben eine Art DAO-Schicht. Normalerweise reicht die Implementierung von mehreren Klassen ohne abstrakte Basisklasse bis zu einer Art generierter Klasse. Eines ist jedoch konsistent - es gibt immer mehr als eine . Höchstwahrscheinlich besteht eine Eins-zu-Eins-Beziehung zwischen den DAOs und den Entitäten im System.

Abhängig von der Ebene der beteiligten Generika können die tatsächlichen Implementierungen von stark dupliziertem Code bis fast leer variieren, wobei der Großteil der Logik in einer abstrakten Basisklasse gruppiert ist.

Diese mehreren Implementierungen können normalerweise durch ein einzelnes parametrisiertes DAO ersetzt werden. Wir können dies so implementieren, dass keine Funktionalität verloren geht, indem wir die von Java Generics bereitgestellte Typensicherheit voll ausnutzen.

Als nächstes werden zwei Implementierungen dieses Konzepts gezeigt, eine für eine auf den Ruhezustand ausgerichtete Persistenzschicht und eine für JPA. Diese Implementierungen sind keineswegs vollständig, aber wir können problemlos weitere zusätzliche Datenzugriffsmethoden hinzufügen.

2.1. Das abstrakte Ruhezustand DAO

Werfen wir einen kurzen Blick auf die AbstractHibernateDao- Klasse:

public abstract class AbstractHibernateDao { private Class clazz; @Autowired SessionFactory sessionFactory; public void setClazz(Class clazzToSet){ this.clazz = clazzToSet; } public T findOne(long id){ return (T) getCurrentSession().get(clazz, id); } public List findAll() { return getCurrentSession().createQuery("from " + clazz.getName()).list(); } public T create(T entity) { getCurrentSession().saveOrUpdate(entity); return entity; } public T update(T entity) { return (T) getCurrentSession().merge(entity); } public void delete(T entity) { getCurrentSession().delete(entity); } public void deleteById(long entityId) { T entity = findOne(entityId); delete(entity); } protected Session getCurrentSession() { return sessionFactory.getCurrentSession(); } }

Dies ist eine abstrakte Klasse mit mehreren Datenzugriffsmethoden, die die SessionFactory zum Bearbeiten von Entitäten verwendet.

2.2. Das generische DAO für den Ruhezustand

Nachdem wir die abstrakte DAO-Klasse haben, können wir sie nur einmal erweitern. Die generische DAO-Implementierung wird die einzige Implementierung sein, die wir benötigen:

@Repository @Scope(BeanDefinition.SCOPE_PROTOTYPE) public class GenericHibernateDao extends AbstractHibernateDao implements IGenericDao{ // }

Erstens, beachten Sie, dass die generische Implementierung selbst parametriert , so dass der Kunde die richtigen Parameter auf einem von Fall zu Fall entscheiden. Dies bedeutet, dass die Clients alle Vorteile der Typensicherheit nutzen können, ohne mehrere Artefakte für jede Entität erstellen zu müssen.

Beachten Sie zweitens den Prototypumfang dieser generischen DAO-Implementierung . Die Verwendung dieses Bereichs bedeutet, dass der Spring-Container bei jeder Anforderung eine neue Instanz des DAO erstellt (auch beim automatischen Verdrahten). Auf diese Weise kann ein Dienst je nach Bedarf mehrere DAOs mit unterschiedlichen Parametern für unterschiedliche Entitäten verwenden.

Der Grund, warum dieser Bereich so wichtig ist, liegt in der Art und Weise, wie Spring Bohnen im Container initialisiert. Das generische DAO ohne Gültigkeitsbereich zu belassen, würde bedeuten, den Standard-Singleton-Gültigkeitsbereich zu verwenden, was dazu führen würde, dass eine einzelne Instanz des DAO im Container lebt . Dies wäre offensichtlich für jede Art von komplexerem Szenario äußerst einschränkend.

Das IGenericDao ist einfach eine Schnittstelle für alle DAO-Methoden, damit wir die Implementierung implementieren können, die wir benötigen:

public interface IGenericDao { T findOne(final long id); List findAll(); void create(final T entity); T update(final T entity); void delete(final T entity); void deleteById(final long entityId); }

2.3. Die Zusammenfassung JPA DAO

Das AbstractJpaDao ist dem AbstractHibernateDao sehr ähnlich :

public abstract class AbstractJpaDao { private Class clazz; @PersistenceContext EntityManager entityManager; public void setClazz( Class clazzToSet ) { this.clazz = clazzToSet; } public T findOne( Long id ){ return entityManager.find( clazz, id ); } public List findAll(){ return entityManager.createQuery( "from " + clazz.getName() ) .getResultList(); } public void save( T entity ){ entityManager.persist( entity ); } public void update( T entity ){ entityManager.merge( entity ); } public void delete( T entity ){ entityManager.remove( entity ); } public void deleteById( Long entityId ){ T entity = getById( entityId ); delete( entity ); } }

Ähnlich wie bei der Hibernate DAO-Implementierung verwenden wir die Java Persistence API direkt, ohne auf das jetzt veraltete Spring JpaTemplate angewiesen zu sein .

2.4. Das generische JPA DAO

Ähnlich wie bei der Hibernate-Implementierung ist auch das JPA-Datenzugriffsobjekt unkompliziert:

@Repository @Scope( BeanDefinition.SCOPE_PROTOTYPE ) public class GenericJpaDao extends AbstractJpaDao implements IGenericDao{ // }

3. Injizieren dieses DAO

Wir haben jetzt eine einzige DAO-Schnittstelle, die wir injizieren können. Wir müssen auch die Klasse angeben :

@Service class FooService implements IFooService{ IGenericDao dao; @Autowired public void setDao(IGenericDao daoToSet) { dao = daoToSet; dao.setClazz(Foo.class); } // ... }

Spring verdrahtet die neue DAO-Instanz automatisch mithilfe der Setter-Injection, sodass die Implementierung mit dem Class- Objekt angepasst werden kann . Nach diesem Zeitpunkt ist das DAO vollständig parametrisiert und kann vom Dienst verwendet werden.

Es gibt natürlich auch andere Möglichkeiten, wie die Klasse für das DAO angegeben werden kann - über Reflektion oder sogar in XML. Ich bevorzuge diese einfachere Lösung aufgrund der verbesserten Lesbarkeit und Transparenz im Vergleich zur Verwendung von Reflexion.

4. Fazit

In diesem Artikel wurde die Vereinfachung der Datenzugriffsschicht durch Bereitstellung einer einzigen wiederverwendbaren Implementierung eines generischen DAO erläutert. Wir haben die Implementierung sowohl in einer Hibernate- als auch in einer JPA-basierten Umgebung gezeigt. Das Ergebnis ist eine optimierte Persistenzschicht ohne unnötige Unordnung.

Eine schrittweise Einführung zum Einrichten des Spring-Kontexts mithilfe der Java-basierten Konfiguration und des grundlegenden Maven-POM für das Projekt finden Sie in diesem Artikel.

Der Code für diesen Artikel befindet sich schließlich im GitHub-Projekt.