spring学习:与JPA的实现集成

来源:互联网 发布:java看书能学会吗 编辑:程序博客网 时间:2024/06/07 10:16

简介

    在前一篇文章里我们讨论了spring4和 hibernate3, hibernate4的集成。在前面示例中通过这种方式访问数据库有一些可以改进的地方。一个是原来的service实现里直接关联了sessionFactory,实际上在service这个业务的层面不应该关注具体的数据存储操作。另外一个就是使用hibernate导致和它的紧密绑定。如果以后我们想要用其他的orm框架的话,还是有一些麻烦。于是这里针对这几个方面做一些改进。

结构改造

    我们先来看第一个问题,在原来的示例里,ContactServiceImpl是直接引用了sessionFactory。如果我们仔细思考一下,会发现这里有一些可以改进的地方。首先一个,我们对数据的操作可以放到专门定义的DAO包里。这样我们还需要定义一个通用的接口。在这个通用的接口里定义最常用的CRUD操作。然后对于不同类的具体数据访问,我们可以再继承这个接口实现特定的类。按照这个思路,我们定义后面的类结构如下图:

    如前面所述,接口DAO是一个泛型的接口,它针对的是通用的数据类型的CRUD。针对具体示例中Contact类型,它有一个专门的ContactDao接口,除了类型特别针对Contact以外,它还包含了对于Contact数据的特别操作,比如findByEmail。而对于前面Dao接口的一个通用实现就放在抽象类AbstractHbnDao里。我们要实现的类HbnContactDao只需要继承它就自动获得了基本的CRUD功能了。

    按照这个思路的具体实现如下:

    Dao:

    

Java代码  收藏代码
  1. package com.yunzero.dao;  
  2.   
  3. import java.io.Serializable;  
  4. import java.util.List;  
  5.   
  6. public interface Dao<T extends Object> {  
  7.       
  8.     void create(T t);  
  9.       
  10.     T get(Serializable id);  
  11.       
  12.     T load(Serializable id);  
  13.       
  14.     List<T> getAll();  
  15.       
  16.     void update(T t);  
  17.       
  18.     void delete(T t);  
  19.       
  20.     void deleteById(Serializable id);  
  21.       
  22.     void deleteAll();  
  23.       
  24.     long count();  
  25.       
  26.     boolean exists(Serializable id);  
  27. }  
     这里通用的地方就在于它是采用泛型的类型。还要一个值得注意的地方就是里面get, load, delete,exists等方法的参数是使用Serializable类型。这是因为我们在定义数据库表的键值时通常选用int, long等类型。而在java里,Integer, Long类型都是实现Serializable接口的,可以更通用一些。

 

    AbstractHbnDao的实现如下:

 

Java代码  收藏代码
  1. public abstract class AbstractHbnDao<T extends Object> implements Dao<T> {  
  2.     @PersistenceContext  
  3.     private EntityManager entityManager;  
  4.     private Class<T> domainClass;  
  5.       
  6.     protected Session getSession() {  
  7.         return entityManager;  
  8.     }  
  9.       
  10.     @SuppressWarnings("unchecked")  
  11.     private Class<T> getDomainClass() {  
  12.         if (domainClass == null) {  
  13.             ParameterizedType thisType = (ParameterizedType) getClass().getGenericSuperclass();  
  14.             this.domainClass = (Class<T>) thisType.getActualTypeArguments()[0];  
  15.         }  
  16.         return domainClass;  
  17.     }  
  18.       
  19.     private String getDomainClassName() { return getDomainClass().getName(); }  
  20.       
  21.     @Override  
  22.     public void create(T t) {  
  23.           
  24.         // If there's a setDateCreated() method, then set the date.  
  25.         Method method = ReflectionUtils.findMethod(  
  26.                 getDomainClass(), "setDateCreated"new Class[] { Date.class });  
  27.         if (method != null) {  
  28.             try {  
  29.                 method.invoke(t, new Date());  
  30.             } catch (Exception e) {  
  31.                 // Ignore any exception here; simply abort the setDate() attempt  
  32.             }  
  33.         }  
  34.           
  35.         getSession().save(t);  
  36.     }  
  37.       
  38.     @Override  
  39.     @SuppressWarnings("unchecked")  
  40.     public T get(Serializable id) {  
  41.         return (T) getSession().get(getDomainClass(), id);  
  42.     }  
  43.       
  44.     @Override  
  45.     @SuppressWarnings("unchecked")  
  46.     public T load(Serializable id) {  
  47.         return (T) getSession().load(getDomainClass(), id);  
  48.     }  
  49.       
  50.     @SuppressWarnings("unchecked")  
  51.     public List<T> getAll() {  
  52.         return getSession()  
  53.             .createQuery("from " + getDomainClassName())  
  54.             .list();  
  55.     }  
  56.       
  57.     @Override  
  58.     public void update(T t) { getSession().update(t); }  
  59.       
  60.     @Override  
  61.     public void delete(T t) { getSession().delete(t); }  
  62.       
  63.     @Override  
  64.     public void deleteById(Serializable id) { delete(load(id)); }  
  65.       
  66.     @Override  
  67.     public void deleteAll() {  
  68.         getSession()  
  69.             .createQuery("delete " + getDomainClassName())  
  70.             .executeUpdate();  
  71.     }  
  72.       
  73.     @Override  
  74.     public long count() {  
  75.         return (Long) getSession()  
  76.             .createQuery("select count(*) from " + getDomainClassName())  
  77.             .uniqueResult();  
  78.     }  
  79.       
  80.     @Override  
  81.     public boolean exists(Serializable id) { return (get(id) != null); }  
  82. }  
    有了这个基础,HbnContactDao的实现就很简单了:
Java代码  收藏代码
  1. package com.yunzero.dao.hibernate;  
  2.   
  3. import static org.springframework.util.Assert.notNull;  
  4.   
  5. import java.util.List;  
  6.   
  7. import org.springframework.stereotype.Repository;  
  8.   
  9. import com.yunzero.dao.ContactDao;  
  10. import com.yunzero.model.Contact;  
  11.   
  12. @Repository  
  13. public class HbnContactDao extends AbstractHbnDao<Contact> implements ContactDao {  
  14.   
  15.     @Override  
  16.     @SuppressWarnings("unchecked")  
  17.     public List<Contact> findByEmail(String email) {  
  18.         notNull(email, "email can't be null");  
  19.         return getSession()  
  20.             .getNamedQuery("findContactsByEmail")  
  21.             .setString("email""%" + email + "%")  
  22.             .list();  
  23.     }  
  24. }  

jpa

    前面的实现里已经把jpa的部分东西给用起来了。hibernate和jpa是一个什么样的关系呢?其实,hibernate最早出来作为一个orm的框架时,它带动了一个标准的成立。就是JPA这个规范。针对jpa这个规范的实现有很多,除了hibernate本身,还要eclipselink, openjpa等。所以,如果我们在实现中需要考虑不同的jpa实现时,为了保持这种灵活性,尽量使得使用的包和类是jpa 规范定义的。

    在前面的代码里,我们就可以看到差别。以前的示例是通过配置sessionFactory,而这里是配置的entityManager。因为我们实际上还是使用的hibernate,只是按照jpa规范的方式来用。这里就需要修改一下配置。

 

配置

     具体的配置文件如下:

Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:c="http://www.springframework.org/schema/c"  
  4.     xmlns:p="http://www.springframework.org/schema/p"  
  5.     xmlns:aop="http://www.springframework.org/schema/aop"  
  6.     xmlns:context="http://www.springframework.org/schema/context"  
  7.     xmlns:tx="http://www.springframework.org/schema/tx"  
  8.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  9.     xsi:schemaLocation="http://www.springframework.org/schema/beans   
  10.         http://www.springframework.org/schema/beans/spring-beans.xsd  
  11.         http://www.springframework.org/schema/aop   
  12.         http://www.springframework.org/schema/aop/spring-aop-4.1.xsd  
  13.         http://www.springframework.org/schema/tx   
  14.         http://www.springframework.org/schema/tx/spring-tx-4.1.xsd  
  15.         http://www.springframework.org/schema/context  
  16.         http://www.springframework.org/schema/context/spring-context-4.1.xsd">  
  17.           
  18.     <context:property-placeholder location="classpath:/environment.properties" />  
  19.       
  20.     <bean id="dataSource"  
  21.         class="org.apache.commons.dbcp2.BasicDataSource"  
  22.         destroy-method="close"  
  23.         p:driverClassName="${dataSource.driverClassName}"  
  24.         p:url="${dataSource.url}"  
  25.         p:username="${dataSource.username}"  
  26.         p:password="${dataSource.password}" />  
  27.   
  28.     <bean id="entityManagerFactory"  
  29.         class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"  
  30.         p:dataSource-ref="dataSource"  
  31.         p:packagesToScan="com.yunzero.model">  
  32.           
  33.         <property name="persistenceProvider">  
  34.             <bean class="org.hibernate.jpa.HibernatePersistenceProvider" />  
  35.         </property>  
  36.         <property name="jpaProperties">  
  37.             <props>  
  38.                 <prop key="hibernate.hbm2ddl.auto">update</prop>  
  39.                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>  
  40.                 <prop key="hibernate.show_sql">false</prop>  
  41.             </props>  
  42.         </property>  
  43.     </bean>  
  44.       
  45.     <bean id="transactionManager"  
  46.         class="org.springframework.orm.jpa.JpaTransactionManager"  
  47.         p:entityManagerFactory-ref="entityManagerFactory" />  
  48.       
  49.     <tx:annotation-driven />  
  50.       
  51.     <!-- These automatically register the PersistenceAnnotationBeanPostProcessor, as indicated above. -->  
  52.     <context:component-scan base-package="com.yunzero.dao.jpa" />  
  53.     <context:component-scan base-package="com.yunzero.service.impl" />  
  54.   
  55. </beans>  

   这里配置的要点是entityManagerFactory,它实际上对应org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean,而使用的persistenceProvider是org.hibernate.jpa.HibernatePersistenceProvider这个类。

    还要一个比较有意思的地方就是配置项hibernate.hbm2ddl.auto,它配置的值为update。使用这个属性有一个作用。就是它可以自动生成数据库的表。除了update这个配置项,还要其他的选项。有了这个配置的话,当数据库里不存在对应的数据库表的话,它会自动生成表。这种方式带来的一个好处就是如果所有表都是通过orm自动生成的,它对于数据库的迁移有很大的好处。不需要人为的去考虑不同数据库平台的变化。每次选择好对应的数据库驱动就可以了 。另外,这种方式使得我们只需要关注领域模型的定义,对应的数据库表自动生成了。使用过ruby on rails或者django的同学会更加有深刻的体会。

总结

     spring和jpa实现的集成相当于一种官方规范的实现。它可以实现不同orm框架之间的切换而且不需要修改一行代码。最关键的是当使用spring和jpa框架集成的时候,需要考虑数据访问层的抽象和结构。里面的巧妙之处值得仔细推敲。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 3个月没有来月经怎么办 48岁突然没月经怎么办 月经来了疼的厉害怎么办 月经肚子疼怎么办快速止疼 来月经疼的厉害怎么办 22岁闭经6个月怎么办 月经推迟16天了怎么办 月经半年没来了怎么办 月经推迟两个月了还不来怎么办 四个月月经不来怎么办 快两个月没来月经了怎么办 月经停了两个月怎么办 别人诬告我我该怎么办 有人造谣我我该怎么办 宝宝晚上不睡觉哭闹怎么办 婴儿晚上不睡觉哭闹怎么办 2月宝宝排便困难怎么办 3岁宝宝老是哭闹怎么办 2岁了囟门闭合晚怎么办 宝宝卤门闭合慢怎么办 手经常碰水脱皮怎么办 迅雷文件已移除怎么办 手机不读sd卡怎么办 g买卖卖错账号怎么办 森林被野人拖走怎么办 我的世界没有羊怎么办 黑魂3杀死铁匠后怎么办 幻境7下8走错了怎么办 换了手机号微信怎么办 微信游戏没了怎么办 找sf网站被劫持怎么办 护发精油抹多了怎么办 用了护发素洗头怎么办 电脑c盘空间不足怎么办 把水蛭吃肚子里怎么办 不小心喝到蚂蟥怎么办 水蛭喝进肚子里怎么办 蚂蝗钻入皮肤里怎么办 孩子屁眼红疼怎么办啊 宝宝屁眼红疼怎么办4岁 屁股眼上火很疼怎么办