spring学习:与JPA的实现集成
简介
在前一篇文章里我们讨论了spring4和 hibernate3, hibernate4的集成。在前面示例中通过这种方式访问数据库有一些可以改进的地方。一个是原来的service实现里直接关联了sessionFactory,实际上在service这个业务的层面不应该关注具体的数据存储操作。另外一个就是使用hibernate导致和它的紧密绑定。如果以后我们想要用其他的orm框架的话,还是有一些麻烦。于是这里针对这几个方面做一些改进。
结构改造
我们先来看第一个问题,在原来的示例里,ContactServiceImpl是直接引用了sessionFactory。如果我们仔细思考一下,会发现这里有一些可以改进的地方。首先一个,我们对数据的操作可以放到专门定义的DAO包里。这样我们还需要定义一个通用的接口。在这个通用的接口里定义最常用的CRUD操作。然后对于不同类的具体数据访问,我们可以再继承这个接口实现特定的类。按照这个思路,我们定义后面的类结构如下图:
如前面所述,接口DAO是一个泛型的接口,它针对的是通用的数据类型的CRUD。针对具体示例中Contact类型,它有一个专门的ContactDao接口,除了类型特别针对Contact以外,它还包含了对于Contact数据的特别操作,比如findByEmail。而对于前面Dao接口的一个通用实现就放在抽象类AbstractHbnDao里。我们要实现的类HbnContactDao只需要继承它就自动获得了基本的CRUD功能了。
按照这个思路的具体实现如下:
Dao:
package com.yunzero.dao; import java.io.Serializable; import java.util.List; public interface Dao<T extends Object> { void create(T t); T get(Serializable id); T load(Serializable id); List<T> getAll(); void update(T t); void delete(T t); void deleteById(Serializable id); void deleteAll(); long count(); boolean exists(Serializable id); }这里通用的地方就在于它是采用泛型的类型。还要一个值得注意的地方就是里面get, load, delete,exists等方法的参数是使用Serializable类型。这是因为我们在定义数据库表的键值时通常选用int, long等类型。而在java里,Integer, Long类型都是实现Serializable接口的,可以更通用一些。
AbstractHbnDao的实现如下:
public abstract class AbstractHbnDao<T extends Object> implements Dao<T> { @PersistenceContext private EntityManager entityManager; private Class<T> domainClass; protected Session getSession() { return entityManager; } @SuppressWarnings("unchecked") private Class<T> getDomainClass() { if (domainClass == null) { ParameterizedType thisType = (ParameterizedType) getClass().getGenericSuperclass(); this.domainClass = (Class<T>) thisType.getActualTypeArguments()[0]; } return domainClass; } private String getDomainClassName() { return getDomainClass().getName(); } @Override public void create(T t) { // If there's a setDateCreated() method, then set the date. Method method = ReflectionUtils.findMethod( getDomainClass(), "setDateCreated", new Class[] { Date.class }); if (method != null) { try { method.invoke(t, new Date()); } catch (Exception e) { // Ignore any exception here; simply abort the setDate() attempt } } getSession().save(t); } @Override @SuppressWarnings("unchecked") public T get(Serializable id) { return (T) getSession().get(getDomainClass(), id); } @Override @SuppressWarnings("unchecked") public T load(Serializable id) { return (T) getSession().load(getDomainClass(), id); } @SuppressWarnings("unchecked") public List<T> getAll() { return getSession() .createQuery("from " + getDomainClassName()) .list(); } @Override public void update(T t) { getSession().update(t); } @Override public void delete(T t) { getSession().delete(t); } @Override public void deleteById(Serializable id) { delete(load(id)); } @Override public void deleteAll() { getSession() .createQuery("delete " + getDomainClassName()) .executeUpdate(); } @Override public long count() { return (Long) getSession() .createQuery("select count(*) from " + getDomainClassName()) .uniqueResult(); } @Override public boolean exists(Serializable id) { return (get(id) != null); } }有了这个基础,HbnContactDao的实现就很简单了: