Vereinfachen Sie das DAO mit Spring und Java Generics

Vereinfachen Sie das DAO mit Spring und Java Generics

1. Überblick

Dieser Artikel konzentriert sich aufsimplifying the DAO layer, indem ein einzelnes generiertes Datenzugriffsobjekt für alle Entitäten im System verwendet wird. Dies führt zu einem eleganten Datenzugriff ohne unnötige Unordnung oder Ausführlichkeit.

Wir werden auf der Abstract DAO-Klasse aufbauen, die wir inour previous article in Spring und Hibernate gesehen haben, und Generika-Unterstützung hinzufügen.

Weitere Lektüre:

Einführung in Spring Data JPA

Einführung in Spring Data JPA mit Spring 4 - die Spring-Konfiguration, das DAO, manuelle und generierte Abfragen und das Transaktionsmanagement.

Read more

Ein Leitfaden für JPA mit Frühling

Einrichten von JPA mit Spring - So richten Sie die EntityManager-Factory ein und verwenden die unformatierten JPA-APIs.

Read more

Prüfung mit JPA, Hibernate und Spring Data JPA

Dieser Artikel beschreibt drei Ansätze zur Einführung der Überwachung in eine Anwendung: JPA, Hibernate Envers und Spring Data JPA.

Read more

[more-722] #

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 generierten Klasse. Eines ist jedoch konsistent - es gibtalways more than one. 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.

These multiple implementations can usually be replaced by a single parametrized DAO. 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 die andere für JPA. Diese Implementierungen sind auf keinen Fall vollständig, aber wir können einfach weitere Datenzugriffsmethoden hinzufügen.

2.1. Der abstrakte Hibernate DAO

Werfen wir einen kurzen Blick auf die KlasseAbstractHibernateDao:

public abstract class AbstractHibernateDao {
    private Class clazz;

    @Autowired
    SessionFactory sessionFactory;

    public void setClazz(Class< T > 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, dieSessionFactory zum Bearbeiten von Entitäten verwendet.

2.2. Das generische DAO für den Ruhezustand

Jetzt, da wir die abstrakte DAO-Klasse haben, können wir sie nur einmal erweitern. The generic DAO implementation will become the only implementation brauchen wir:

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

Erstensnote that the generic implementation is itself parameterized, damit der Client von Fall zu Fall den richtigen Parameter auswählen kann. Dies bedeutet, dass die Clients alle Vorteile der Typensicherheit nutzen können, ohne mehrere Artefakte für jede Entität erstellen zu müssen.

Zweitens beachten Siethe prototype scope of this generic DAO implementation. 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 bei Bedarf mehrere DAOs mit unterschiedlichen Parametern für verschiedene Entitäten verwenden.

Der Grund, warum dieser Bereich so wichtig ist, liegt in der Art und Weise, wie Spring Beans im Container initialisiert. Wenn Sie das generische DAO ohne Gültigkeitsbereich belassen, müssen Siethe default singleton scope, which would lead to a single instance of the DAO living in the container verwenden. Das wäre natürlich für jede Art von komplexerem Szenario äußerst restriktiv.

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

DasAbstractJpaDao ist demAbstractHibernateDao: sehr ähnlich

public abstract class AbstractJpaDao< T extends Serializable > {

   private Class< T > clazz;

   @PersistenceContext
   EntityManager entityManager;

   public void setClazz( Class< T > clazzToSet ) {
      this.clazz = clazzToSet;
   }

   public T findOne( Long id ){
      return entityManager.find( clazz, id );
   }
   public List< T > 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 uns auf die jetzt veralteten SpringJpaTemplatezu verlassen.

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< T extends Serializable >
 extends AbstractJpaDao< T > implements IGenericDao< T >{
   //
}

3. Injizieren dieses DAO

We now have a single DAO interface we can inject. Wir müssen auchClass: angeben

@Service
class FooService implements IFooService{

   IGenericDao dao;

   @Autowired
   public void setDao(IGenericDao daoToSet) {
      dao = daoToSet;
      dao.setClazz(Foo.class);
   }

   // ...
}

Springautowires the new DAO instance using setter injection, damit die Implementierung mit demClass-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 Reflection oder sogar in XML. Ich bevorzuge diese einfachere Lösung aufgrund der besseren Lesbarkeit und Transparenz im Vergleich zur Verwendung der Reflexion.

4. Fazit

In diesem Artikel wurden diesimplification of the Data Access Layer erläutert, indem eine einzelne, wiederverwendbare Implementierung eines generischen DAO bereitgestellt wurde. Wir haben die Implementierung sowohl in einer Hibernate- als auch in einer JPA-basierten Umgebung gezeigt. Das Ergebnis ist eine optimierte Persistenzschicht ohne unnötiges Durcheinander.

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 unterthis article.

Schließlich kann der Code für diesen Artikel inthe GitHub project gefunden werden.