基于JPA规范的通用泛型DAO模式研究

2017-01-21 14:39陈道远
软件导刊 2016年12期

陈道远

摘 要:DAO模式是标准的J2EE设计模式之一,是持久层的标准模式。实现DAO模式不但屏蔽了数据存储最终介质的不同,也屏蔽了实现技术的不同。分析了传统DAO模式设计,阐述了泛型程序设计和JPA规范,提出了一种将泛型程序设计以及JPA规范引入DAO模式的设计思路,给出了在Spring框架中基于JPA规范实现通用泛型DAO的方法。

关键词:DAO模式;JPA规范;泛型程序设计

DOIDOI:10.11907/rjdk.162179

中图分类号:TP301

文献标识码:A文章编号:1672-7800(2016)012-0011-03

0 引言

DAO(Data Acess Object)模式是标准的J2EE设计模式之一,开发人员使用这个模式把底层数据访问操作和上层业务逻辑分开。泛型是JDK1.5的新特性,使用泛型机制编写的程序代码要比那些杂乱使用Object变量再进行强制类型转换的代码安全性和可读性更好。JPA规范能够简化现有Java EE和Java SE应用的对象持久化开发工作,整合ORM(Object Relational Mapping) 对象关系映射技术。

实际应用中,开发者一般在框架提供的支持类基础上编写自己的DAO基类。本文提出了一种在Spring框架中基于JPA规范的通用泛型DAO模式设计方法。

1 DAO模式设计泛型引入

1.1 传统DAO模式设计

DAO模式已经成为持久层的标准模式,它的灵活性首先要归功于一个对象设计的最佳实践:针对接口编程[1]。这一原则规定实体必须实现一个调用程序而不是实体自身使用的接口。因此,可以轻松替换不同的实现而对客户端代码产生很小的影响,如图1所示。

图1是一个典型的DAO应用实例。在UserDAO中定义访问User数据对象的接口方法,业务层通过接口UserDAO操作数据,并使用持久化技术实现UserDAO接口方法,比如Jdbc、Hibernate等的具体实现,这样业务层和具体持久化技术就实现了解耦。

1.2 泛型程序设计

Java5.0的新特性之一是引入了泛型[3],泛型允许编写可作用于任意类型的类,但是直到声明了类的实例,才指定特定的类型。因为此类型不是作为类定义的一部分而指定的,所以该类一般对任意指定类型起作用。

大量使用泛型的例子是集合类。例如ArrayList可以声明只容纳String,这样就增加了类型安全性。

1.3 将泛型引入DAO模式

虽然Spring通过模板和支持类为各种ORM框架提供了出色支持,但在开发过程中发现存在不足。首先传统DAO模式代码经常出现重复,开发效率不高;其次,由于支持类的一些方法使代码显得比较拖沓;第三,在没有泛型支持的情况下,不但要指定主键值还要指定实体类型,调用者还需要对返回结果对象强制进行类型转换。这些缺点都可通过在基类中引入泛型得到彻底解决。

构建泛型DAO是类型安全、代码精简的设计模式(相对于传统DAO),尤其在DAO组件数量庞大时,代码量的减少更加明显,它对项目成本、时间安排和工作产生明显的负面影响。实际应用中,在Spring支持类的基础上利用泛型编程编写自己的DAO基类,进行自封装,可以达到改进DAO模式设计的目的。

2 数据库访问中JPA规范的运用

2.1 JPA规范概述

JPA(Java Persistence API)是Java EE新的持久化标准规范[4],它充分借鉴、吸收现有ORM产品和框架,将基于POJO(Plain Old Java Object)简单的Java对象模型ORM技术标准化,成为在J2EE和J2SE环境中通用的Java持久化API。

2.2 JPA技术规范

JPA通过JDK注解或XML描述对象——关系表的映射关系,将运行期的实体对象持久化到数据库中。JPA包括以下3方面技术:①ORM映射元数据。JPA支持两种形式的元数据,分别是JDK注解和XML形式;②JPA 的体系结构及API。运用JPA访问数据库时,首先利用EnitityManager工厂类创建EntityManager,然后开始事务进行持久化操作,最后提交和关闭EntityManager;③查询语言。JPA 独特的 JPQL(Java Persistence Query Language)是 EJB QL 的扩展,它是针对实体的一种查询语言。

2.3 Spring框架的JPA支持类

实际开发中可以使用Spring的JpaDaoSupport,它为JpaTemplate模板提供了支持类,为使用JPA的DAO提供了方便。可以设置实体管理器工厂,通过它获取JpaTemplate模板实例,并调用相应方法进行数据访问和持久化工作。

3 基于JPA规范的泛型DAO实现

实际开发中,将DAO基类的设计基于JPA规范,并和泛型编程结合,构建一个通用的DAO层。

3.1 相关配置

集成JPA规范需要编写相应的配置信息,主要包括3个部分:①在Spring框架中通过LocalEntityManagerFactoryBean支持类来配置一个EnityManagerFactory Bean;②系统需要在META-INF/persistence.xml文件中配置JPA持久化环境;③必须提供实体的元数据信息。

在Spring的配置文件applicationContext.xml中,需要配置持久化单元:

上述代码中StrongWEBPU表示一个持久化单元名称,容器启动时根据persistence.xml文件中定义的JPA环境信息建好实例,生成有关对象。

配置时采用Hibernate实现JPA规范[5],也可以换成其它形式的ORM层框架。

3.2 通用基础接口设计

一般的实体都必须拥有CRUD等常规操作,可以提供一个通用的DAO接口,避免在每个实体DAO接口中重复定义这些方法。实体DAO接口可以继承这个通用DAO接口,并可以定义实体类相关的其它操作方法,对于不常用的方法则要求子类显式调用模板实例完成。DAO基类部分代码如下:

public interface IBaseDao

//泛型支持,可加载不同实体

{

public void save(T transientInstance);

public T update(T detachedInstance);

public void delete(T persistentInstance);

public void deleteAll();

public List findByExample(T example);

//返回泛型集合的结果

public Integer getCountAll();

public List findByJPQL(final String strJPQL);

public List findByJPQL(final int intBegin,final int intMax,final String strJPQL,final Object...objParams);

//构建复杂的多条件查询语句

………

}

实体类开发时,需要定义相应接口,可继承基础接口,只需加入实体特定的成员变量和方法。假设一个用户(User)实体接口代码如下:

public interface IUserDAO extends IBaseDAO {

public List findByName(String name);

//实体特有的方法 ………

}

可以看出,它不需要再定义CRUD等常见方法,只需要增加一些个性化的方法。

3.3 DAO基类设计

DAO基类继承于JpaDaoSupport,并实现通用基础接口。此外,还可以在该类中提供更多方便的操作方法降低子类的实现难度。这样,子类仅需要指定泛型对应的实体类就拥有了各种通用的数据操作能力,通过反射机制获取泛型对应的实体类型。如果实体DAO的数据操作仅是一些常见的CRUD操作,子类甚至可以不编写任何代码,使编码效率得到极大提高,抽象性上升了一个台阶。

示例代码如下:

public class BaseDAO extends JpaDaoSupport implements IBaseDAO

//继承JPA规范的DAO支持类,实现泛型DAO接口

{

private Class persistentClass;

//实体类的类型

public StrongDAO()

//通过反射机制获取泛型对应的实体类的类型

{

this.persistentClass=(Class) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

}

public void save(T transientInstance){

Assert.notNull(transientInstance);

try {

//使用JpaTemPlate模板类的方法

getJpaTemplate().persist(transientInstance); } catch (RuntimeException re) {

throw re;

} }

public void delete(T persistentInstance) {

Assert.notNull(persistentInstance);

try {

//使用JpaTemPlate模板类的方法

getJpaTemplate().remove(persistentInstance); } catch (RuntimeException re) {

throw re; } }

public Integer executeByJPQL(final String strJPQL)

//使用回调方法获取更多的方法{

Object ret = this.getJpaTemplate().execute(new JpaCallback() {

public Object doInJpa(EntityManager em) throws PersistenceException {

Query query = em.createQuery(strJPQL);

return query.executeUpdate();

}

});

return (Integer) ret;

} ………

}

由上述代码可以看出,DAO基类通过反射机制可以获取不同的实体类型,拥有各种通用的数据操作能力,利用Spring支持的JpaDaoSupport 可实现各种数据操作方法。

在实体类DAO开发时,通过扩展DAO基类,指定具体的实体类,就将自动拥有常见数据操作功能。比如,用户类DAO的实现可以简化到少数几行代码:

public class UserDAO extends BaseDAO implements IUserDAO {

public List findByName(String name) {

//实体特有的方法,其它方法来自基类

return findByProperty(“name”,xxBy1);

}

通过扩展基类,并借助泛型指定实体类,立即拥有了对实例进行CRUD操作的功能,且这些功能都是泛型版本的。按照相似方法,可以对其它不同的实体进行相应设计。引入JpaDaoSupport能够使用基于JPA规范的ORM层框架,可扩展性强,在变更ORM层的具体实现时,只需更改相应配置文件即可。

4 结语

本文利用泛型和JPA规范建立了通用的DAO基类,能够大大减少DAO层代码总量,提高了项目开发效率。

在基础DAO类中实现了CRUD等常用方法,还可以抽象出其它常用方法,如通用的翻页方法等,使其发挥更大的作用。但这种通用DAO模式也存在不足:

(1)这种模式涉及框架比较多,需要进行相关配置。当项目比较小的情况下,可以直接用传统的DAO设计模式,不使用泛型和JPA规范,开发效率会更高。

(2)在开发中对JPQL查询语句没有进行更多优化,在大数据量操作时需要提高运行效率。

DAO层设计是项目开发的重点,然而构建一个通用泛型DAO在不同的项目中有不同的实现,只能抽象出一些共同方法,如通用的翻页方法等,面对不同的项目给出各自的实现。今后要增加一些共同方法,优化基类中JPQL语句,使基类发挥出更大作用。

参考文献:

[1] 计文柯.Spring技术内幕:深入解析Spring架构与设计原理[M].北京:机械工业出版社,2012.

[2] 陈雄华.精通Spring 2.x:企业应用开发详解[M].北京: 电子工业出版社, 2007.

[3] 吴亚峰,纪超.Java SE 6.0 编程指南[M].北京:人民邮电出版社,2007.

[4] 王卫军,楚宁志,吕军.Java软件体系结构设计模式标准指南[M].北京:电子工业出版社,2006.

[5] 白广元.Java Web 整合开发完全自学手册[M].北京:机械工业出版社,2009.

(责任编辑:杜能钢)