Vereenvoudig de DAO met Spring en Java Generics

1. Overzicht

Dit artikel zal zich concentreren op vereenvoudiging van de DAO-laag door een enkel, gegenereerd gegevenstoegangsobject te gebruiken voor alle entiteiten in het systeem, wat resulteert in elegante gegevenstoegang, zonder onnodige rommel of uitgebreidheid.

We zullen voortbouwen op de Abstracte DAO-klasse die we in ons vorige artikel over Spring en Hibernate hebben gezien, en generieke ondersteuning toevoegen.

2. De slaapstand en JPA DAO's

De meeste productiecodebases hebben een soort DAO-laag. Gewoonlijk varieert de implementatie van meerdere klassen zonder abstracte basisklasse tot een soort gegenereerde klasse. Eén ding is echter consistent - er is altijd meer dan één. Hoogstwaarschijnlijk is er een één-op-één-relatie tussen de DAO's en de entiteiten in het systeem.

Afhankelijk van het niveau van de betrokken generieke geneesmiddelen, kunnen de daadwerkelijke implementaties ook variëren van sterk gedupliceerde code tot bijna leeg, waarbij het grootste deel van de logica is gegroepeerd in een abstracte basisklasse.

Deze meerdere implementaties kunnen meestal worden vervangen door een enkele geparametriseerde DAO. We kunnen dit zo implementeren dat er geen functionaliteit verloren gaat door volledig te profiteren van de typeveiligheid die wordt geboden door Java Generics.

We zullen hierna twee implementaties van dit concept laten zien, één voor een Hibernate-centrische persistentielaag en de andere gericht op JPA. Deze implementaties zijn bij lange na niet compleet, maar we kunnen eenvoudig meer aanvullende gegevenstoegangsmethoden toevoegen.

2.1. De abstracte slaapstand DAO

Laten we het SamenvattingHibernatenate klasse:

openbare abstracte klasse AbstractHibernatenate {private Class clazz; @Autowired SessionFactory sessionFactory; openbare void setClazz (Class clazzToSet) {this.clazz = clazzToSet; } openbare T findOne (lange id) {terugkeer (T) getCurrentSession (). get (clazz, id); } openbare lijst findAll () {return getCurrentSession (). createQuery ("van" + clazz.getName ()). list (); } openbare T maken (T-entiteit) {getCurrentSession (). saveOrUpdate (entiteit); terugkeer entiteit; } openbare T-update (T-entiteit) {retour (T) getCurrentSession (). merge (entiteit); } openbare ongeldige verwijdering (T-entiteit) {getCurrentSession (). verwijderen (entiteit); } openbare ongeldige deleteById (lange entiteitId) {T entiteit = findOne (entiteitId); verwijderen (entiteit); } beschermde sessie getCurrentSession () {return sessionFactory.getCurrentSession (); }}

Dit is een abstracte klasse met verschillende gegevenstoegangsmethoden, die de SessionFactory voor het manipuleren van entiteiten.

2.2. De algemene slaapstand DAO

Nu we de abstracte DAO-klasse hebben, kunnen we deze slechts één keer uitbreiden. De generieke DAO-implementatie wordt de enige implementatie wij hebben nodig:

@Repository @Scope (BeanDefinition.SCOPE_PROTOTYPE) openbare klasse GenericHibernateensional breidt uit AbstractHibernatenate implementeert IGenericbao {//}

Eerste, merk op dat de generieke implementatie zelf geparametriseerd is, waardoor de klant de juiste parameter per geval kan kiezen. Dit betekent dat de klanten alle voordelen van typeveiligheid krijgen zonder dat ze voor elke entiteit meerdere artefacten hoeven te maken.

Ten tweede, let op het prototype bereik van deze generieke DAO-implementatie. Het gebruik van dit bereik betekent dat de Spring-container een nieuw exemplaar van de DAO zal maken elke keer dat dit wordt aangevraagd (ook bij autowiring). Hierdoor kan een service indien nodig meerdere DAO's met verschillende parameters voor verschillende entiteiten gebruiken.

De reden dat deze scope zo belangrijk is, is te wijten aan de manier waarop Spring bonen in de container initialiseert. Als u de generieke DAO zonder scope verlaat, moet u de standaard singleton-scope, wat zou leiden tot een enkele instantie van de DAO die in de container leeft. Dat zou uiteraard zeer beperkend zijn voor elk soort complexer scenario.

De IGenericensional is gewoon een interface voor alle DAO-methoden, zodat we de implementatie kunnen injecteren die we nodig hebben:

openbare interface IGenericbao {T findOne (laatste lange id); Lijst findAll (); leegte maken (laatste T-entiteit); T-update (laatste T-entiteit); ongeldig verwijderen (laatste T-entiteit); void deleteById (laatste lange entiteitId); }

2.3. De abstracte JPA DAO

De AbstractJpadred lijkt erg op de Samenvatting

openbare abstracte klasse AbstractJpadred {privé Class clazz; @PersistenceContext EntityManager entityManager; openbare void setClazz (Class clazzToSet) {this.clazz = clazzToSet; } openbare T findOne (lange id) {return entityManager.find (clazz, id); } openbare lijst findAll () {return entityManager.createQuery ("van" + clazz.getName ()) .getResultList (); } public void save (T entity) {entityManager.persist (entiteit); } openbare ongeldige update (T-entiteit) {entityManager.merge (entiteit); } public void delete (T entity) {entityManager.remove (entiteit); } public void deleteById (Long entityId) {T entity = getById (entityId); verwijderen (entiteit); }}

Net als bij de Hibernate DAO-implementatie, gebruiken we de Java Persistence API rechtstreeks, zonder te vertrouwen op de nu verouderde Spring JpaTemplate.

2.4. De generieke JPA DAO

Net als bij de Hibernate-implementatie is het JPA Data Access Object ook eenvoudig:

@Repository @Scope (BeanDefinition.SCOPE_PROTOTYPE) openbare klasse GenericJpaão breidt uit AbstractJpabao implementeert IGenericbao {//}

3. Het injecteren van deze DAO

We hebben nu een enkele DAO-interface die we kunnen injecteren. We moeten ook het Klasse:

@Service klasse FooService implementeert IFooService {IGenericensional dao; @Autowired openbare leegte set Dao (IGenericbao daoToSet) {dao = daoToSet; dao.setClazz (Foo.class); } // ...}

Voorjaar autowires de nieuwe DAO-instantie met behulp van setter-injectie zodat de implementatie kan worden aangepast met de Klasse voorwerp. Na dit punt is de DAO volledig geparametriseerd en klaar om door de service te worden gebruikt.

Er zijn natuurlijk andere manieren waarop de klasse kan worden gespecificeerd voor de DAO - via reflectie of zelfs in XML. Mijn voorkeur gaat uit naar deze eenvoudigere oplossing vanwege de verbeterde leesbaarheid en transparantie in vergelijking met het gebruik van reflectie.

4. Conclusie

In dit artikel werd het vereenvoudiging van de gegevenstoegangslaag door een enkele, herbruikbare implementatie van een generieke DAO te bieden. We hebben de implementatie in zowel een Hibernate- als een JPA-omgeving laten zien. Het resultaat is een gestroomlijnde persistentielaag, zonder onnodige rommel.

Zie dit artikel voor een stapsgewijze introductie over het opzetten van de Spring-context met behulp van op Java gebaseerde configuratie en de basis Maven pom voor het project.

Ten slotte is de code voor dit artikel te vinden in het GitHub-project.