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.
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.
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.
[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.