Permudahkan DAO dengan Spring dan Java Generics

1. Gambaran keseluruhan

Artikel ini akan memfokuskan pada penyederhanaan lapisan DAO dengan menggunakan Objek Akses Data tunggal yang dihasilkan untuk semua entiti dalam sistem, yang akan menghasilkan akses data yang elegan, tanpa kekacauan atau kekacauan yang tidak perlu.

Kami akan membina kelas Abstrak DAO yang kami lihat dalam artikel kami sebelumnya mengenai Spring dan Hibernate, dan menambah sokongan generik.

2. DAO Hibernate dan JPA

Sebilangan besar pangkalan data pengeluaran mempunyai beberapa jenis lapisan DAO. Biasanya, pelaksanaannya berkisar dari beberapa kelas tanpa kelas asas abstrak hingga beberapa jenis kelas generatif. Walau bagaimanapun, satu perkara adalah konsisten - selalu ada lebih daripada satu perkara . Kemungkinan besar, terdapat hubungan satu lawan satu antara DAO dan entiti dalam sistem.

Juga, bergantung pada tahap generik yang terlibat, pelaksanaan sebenarnya boleh berubah dari kod yang banyak digandakan hingga hampir kosong, dengan sebahagian besar logik dikelompokkan dalam kelas dasar abstrak.

Pelbagai implementasi ini biasanya dapat diganti dengan DAO parametrized tunggal. Kami dapat melaksanakannya sehingga tidak ada fungsi yang hilang dengan memanfaatkan sepenuhnya keselamatan jenis yang disediakan oleh Java Generics.

Kami akan menunjukkan dua pelaksanaan konsep ini seterusnya, satu untuk lapisan ketekunan Hibernate centric dan yang lain memfokuskan pada JPA. Pelaksanaan ini sama sekali tidak lengkap, tetapi kami dapat dengan mudah menambahkan lebih banyak kaedah akses data tambahan yang disertakan.

2.1. DAO Hibernate Abstrak

Mari lihat sekilas kelas AbstractHibernateDao :

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(); } }

Ini adalah kelas abstrak dengan beberapa kaedah akses data, yang menggunakan SessionFactory untuk memanipulasi entiti.

2.2. DAO Hibernate Generik

Sekarang kita mempunyai kelas DAO abstrak, kita dapat memperluasnya hanya sekali. Pelaksanaan DAO generik akan menjadi satu-satunya pelaksanaan yang kita perlukan:

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

Pertama, perhatikan bahawa pelaksanaan generik itu sendiri diparamatisasi , yang memungkinkan klien untuk memilih parameter yang betul mengikut kes. Ini bermaksud pelanggan mendapat semua faedah keselamatan jenis tanpa perlu membuat pelbagai artifak untuk setiap entiti.

Kedua, perhatikan skop prototaip pelaksanaan DAO generik ini . Menggunakan skop ini bermaksud bahawa wadah Spring akan membuat contoh baru DAO setiap kali diminta (termasuk pada autowiring). Itu akan membolehkan perkhidmatan menggunakan beberapa DAO dengan parameter yang berbeza untuk entiti yang berbeza, jika diperlukan.

Sebab skop ini sangat penting adalah kerana cara Spring memulakan kacang dalam bekas. Meninggalkan DAO generik tanpa ruang lingkup bermaksud menggunakan ruang lingkup tunggal lalai, yang akan menyebabkan satu contoh DAO tinggal di dalam wadah . Itu jelas akan menjadi sekatan bagi jenis senario yang lebih kompleks.

The IGenericDao adalah semata-mata satu antara muka untuk semua kaedah DAO supaya kita boleh menyuntik pelaksanaan kita perlu:

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. Abstrak JPA DAO

The AbstractJpaDao adalah sangat serupa dengan AbstractHibernateDao:

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 ); } }

Mirip dengan pelaksanaan Hibernate DAO, kami menggunakan Java Persistence API secara langsung, tanpa bergantung pada Spring JpaTemplate yang sudah tidak digunakan lagi .

2.4. DAPA JPA Generik

Sama seperti pelaksanaan Hibernate, Objek Akses Data JPA juga mudah:

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

3. Menyuntik DAO ini

Kami kini mempunyai satu antara muka DAO yang dapat kami suntikan. Kami juga perlu menentukan Kelas:

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

Spring mengaktifkan automatik contoh DAO baru menggunakan setter injection sehingga pelaksanaannya dapat disesuaikan dengan objek Kelas . Selepas tahap ini, DAO sepenuhnya dipadam dan siap digunakan oleh perkhidmatan.

Sudah tentu ada cara lain agar kelas dapat ditentukan untuk DAO - melalui refleksi, atau bahkan dalam XML. Keutamaan saya adalah untuk penyelesaian yang lebih mudah ini kerana pembacaan dan ketelusan yang lebih baik berbanding dengan menggunakan pantulan.

4. Kesimpulan

Artikel ini membincangkan penyederhanaan Lapisan Akses Data dengan menyediakan satu, DAO generik yang dapat digunakan semula. Kami menunjukkan pelaksanaannya di persekitaran berasaskan Hibernate dan JPA. Hasilnya adalah lapisan kegigihan yang diperkemas, tanpa kekacauan yang tidak perlu.

Untuk pengenalan langkah demi langkah mengenai pengaturan konteks Spring menggunakan konfigurasi berdasarkan Java dan Maven pom asas untuk projek ini, lihat artikel ini.

Akhirnya, kod untuk artikel ini boleh didapati di projek GitHub.