原文:JPA implementation patterns: Data Access Objects
作者:Vincent Partington
出处:http://blog.xebia.com/2009/03/09/jpa-implementation-patterns-data-access-objects/
JPA,Java Persistence API的简称,是Java EE 5规范的一部分,并且已经由Hibernate、TopLink、EclipseLink、OpenJPA以及其他的一些对象关系映射(ORM)框架实现。因为JPA的最初目的是作为EJB 3.0规范的组成部分,所以你可以在EJB 3.0应用中使用它,不过在EJB 3.0范围之外它同样好使,例如,可以用在Spring应用中。甚至连Gavin King,Hibernate的设计者也在Hibernate in Action的第二版,又名为Java Persistence with Hibernate一书中建议使用JPA。很显然,JPA已被普遍使用。
一旦你克服了自己对注解(annotation)的恐惧;-),你就会发现外面有大量的资料文献可用来解释API内部的对象和方法、对象一起工作的方式以及如何把它们付诸实现。当你进行hello world类型的程序编写的时候,一切都似乎相当直接明了,但在你开始编写自己的第一个实际应用时,你就会发现,事情并非如此简单。JPA提供的抽象泄漏得厉害,已经影响到了应用的大部分而不仅仅只是影响了数据访问对象(DAO)和领域对象,你需要决定如何处理事务、延迟加载、游离对象(考虑到web框架)、继承等等,其结果是,那些书和文章在这里都不能真正帮到你。
至少,这是我第一次真正开始使用JPA时发现的情况,在未来的几周内,我打算讨论我会面临的选择和我所做的决定,以及为什么我会做出这样的决定。当我完成讨论后,我们会得到一些收获,我愿意不太谦虚地称这些收获为JPA的实施模式。
我们真的需要DAO吗?
那么,让我们从这件事情开始,那就是你可能会开始编写自己的第一个JPA应用:数据访问对象(DAO)。恰好在开始之前,我们需要解决这样一个有趣的问题:在使用JPA的时候,是否需要DAO?一年多之前的讨论结果是“视情况而定”,既然非常难于就这样的一个结论进行争辩J,我想坚持这样的一种想法,即DAO在JPA应用中确实有着自己的位置。可以认为DAO只是在JPA之上提供了很薄的一层,但是其更为重要的是为每个实体类型提供了一个DAO,这一做法带来了这些好处:
不必在每次储存或者加载数据的时候,都要挑选正确的EntityManager方法,一旦决定了使用那一个之后,你和你的整个团队都能够很容易做到坚持这一选择。
可以禁止某些实体类型的某些操作,例如,你可能永远都不希望代码删除日志条目,那么在使用DAO时,只要不把remove方法添加到你的LogEntry DAO中就可以了。
从理论上来说,通过使用DAO你可以切换到另外一个持久性系统(例如普通的JDBC或者iBATIS),但是因为JPA是这样一个有泄漏的抽象,所以我认为甚至对于一个略微复杂的系统来说,这都是不存在切合实际的可能性的。不过,在能够加入跟踪功能或者保持性能统计的地方,你确实是可以获得一个单一的切入点。
可以把所有的查询都集中到某个实体类型上而不是让他们遍布在代码中,可以使用命名查询来保存实体类型的查询,不过仍然需要在一些适中的地方设置正确的参数,把查询、设置参数的代码和到正确返回类型的强制类型转换都放入到DAO中似乎是一件更简单的事情,例如:
public List<ChangePlan> findExecutingChangePlans() {
Query query = entityManager.createQuery(
"SELECT plan FROM ChangePlan plan where plan.state = 'EXECUTING'");
return (List<ChangePlan>) query.getResultList();
}
因此,当你决定要使用DAO时,你如何着手编写它们呢?Spring的JpaTemplate的Javadoc中突出强调(粗体)的注释似乎表明,该特定类在使用上并没有太多的用途,这也使得JpaDaoSupport变成了多余的。作为替代,你可以把自己的JPA DAO编写成使用@PersistenceContext注解来获取EntityManager引用的POJO,它可用在EJB3.0容器中,如果在Spring的上下文中添加了PersistenceAnnotationBeanPostProcessor的话,那么它同样可以用在Spring 2.0以及之上的版本中。
类型安全的泛型DAO模式
因为每个DAO都与其他的DAO共享许多功能,所以有必要创建一个包含了共享功能的基类,然后每个具体的DAO作为子类继承于该基类。网上有许多关于这样一种类型安全的泛型DAO模式的博客文章,你甚至可以从Google Code代码站点上下载一些代码。当把来自所有这些来源的元素综合到一起时,我们得到了以下这一DAO的JPA实施模式:
实体类
比方说,我们希望持久以下这一Order类:
@Entity
@Table(name = "ORDERS")
public class Order {
@Id
@GeneratedValue
private int id;
private String customerName;
private Date date;
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getCustomerName() { return customerName; }
public void setCustomerName(String customerName) { this.customerName = customerName; }
public Date getDate() { return date; }
public void setDate(Date date) { this.date = date;}
}
不要太担心该类的细节,我们会采用其他的JPA实施模式来修正这些细微之处。这里使用了@Table注解是因为ORDER在SQL中是一个保留关键字。
DAO接口
首先定义一个通用的DAO接口,该接口带有我们希望所有DAO共享的方法:
public interface Dao<K, E> {
void persist(E entity);
void remove(E entity);
E findById(K id);
}
第一个类型参数K用来作为键的类型,第2个类型参数E是实体的类型,紧接着基本的persist、remove和findById方法之后,你可能还希望添加一个List findAll()方法。不过与实体类自身的定义相同,我们会用随后的JPA实施模式来修正这些DAO方法。
然后我们为每个要持久的实体类型定义一个子接口,加入任何我们想要的实体特有的方法。例如,如果我们希望能够查询所有自某个特定日期以来已经添加的订单(order)的话,就可以加入这样的一个方法:
public interface OrderDao extends Dao<Integer, Order> {
List<Order> findOrdersSubmittedSince(Date date);
}
基本DAO的实现
第三步是创建一个基础的JPA DAO实现,初步实现我们在步骤1中创建的标准Dao接口的所有方法。
public abstract class JpaDao<K, E> implements Dao<K, E> {
protected Class<E> entityClass;
@PersistenceContext
protected EntityManager entityManager;
public JpaDao() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.entityClass = (Class<E>) genericSuperclass.getActualTypeArguments()[1];
}
public void persist(E entity) { entityManager.persist(entity); }
public void remove(E entity) { entityManager.remove(entity); }
public E findById(K id) { return entityManager.find(entityClass, id); }
}
大部分的实现都相当直接简单,但是有几点要注意:
JpaDao的构造函数包括了我的同事Arjan Blokzijl提议的使用反射来获得实体类的方法。
@PersistenceContext注解会引发EJB 3.0容器或者Spring注入实体管理器。
entityManager和entityClass域被定义成是protected的,这样的话子类,例如特定的DAO实现就能够访问它们。
特定DAO的实现
最后我们来创建一个具体的DAO实现,其扩展了基础的JPA DAO类并实现了特定的DAO接口:
public class JpaOrderDao extends JpaDao<Integer, Order> implements OrderDao {
public List<Order> findOrdersSubmittedSince(Date date) {
Query q = entityManager.createQuery(
"SELECT e FROM " + entityClass.getName() + " e WHERE date >= :date_since");
q.setParameter("date_since", date);
return (List<Order>) q.getResultList();
}
}
使用DAO
如何获得一个到OrderDao实例的引用取决于我们是使用EJB 3.0还是使用Spring,在EJB 3.0中,我们会使用这样的注解:
@EJB(name="orderDao")
private OrderDao orderDao;
而在Spring中我们可以使用XML bean文件或者使用这种方式的自动绑定:
@Autowired
public OrderDao orderDao;
不管是哪种方式,一旦有到DAO的引用,我们就可以这样来使用它:
Order o = new Order();
o.setCustomerName("Peter Johnson");
o.setDate(new Date());
orderDao.persist(o);
不过我们还可以使用添加到OrderDao接口的实体特有的查询:
List<Order> orders = orderDao.findOrdersSubmittedSince(date);
for (Order each : orders) {
System.out.println("order id = " + each.getId());
}
通过使用这一类型安全的DAO模式,我们获得了如下好处:
客户端代码没有直接依赖于JPA的API。
通过使用泛型保证了类型的安全,所有需要完成的强制类型转换都在DAO的实现中被处理了。
在一个合理的地方汇集了所有的实体特有的JPA代码。
可在同一个场所添加事务标志、调试和配置描述等,诚然如我们稍后会见到的那样,我们也需要在应用的其他部分中添加事务标志。
测试数据库访问代码时,只要测试一个类就够了,在后面的某个JPA实施模式中,我们会重新提到这一问题。
我希望这能够说服你,在使用JPA时,确实需要DAO。J
第一个JPA的实施模式到此就介绍完了,在该系列接下来的博客文章中,我们将以这一例子为基础来讨论下一个模式,在此期间我将非常乐意倾听你是如何编写自己的DAO的!
分享到:
相关推荐
一共有三个分卷。全部下载才能解压。 这本书不错,值得一看。
ORM解决了传统的JDBC编程模式中数据访问的繁琐性,提高了开发效率。在Hibernate中,实体类代表数据库表,实体类的实例对应表中的记录,属性对应字段,这样就将复杂的SQL操作转化为简单的对象操作。 其次,书中详细...
Spring Data JPA 是一款基于 Java 持久层 API(JPA)的框架,提供了一个通用的数据访问层解决方案,能够简化 Java 应用程序中的数据访问工作。Spring Boot 作为一个流行的 Java 框架,提供了一个快速开发的平台,...
至于“工具”标签,可能是指Spring Data JPA简化了数据库操作,使得开发者无需手动编写大量的DAO(数据访问对象)和SQL,从而提高了开发效率。此外,Spring Data JPA还可以与Spring Boot整合,实现自动化配置,使得...
**JPA(Java Persistence API)**是Java平台上的一个标准,用于管理关系数据库中的数据,它简化了数据库操作,提供了一种面向对象的方式来处理数据库事务。JPA通过ORM(Object-Relational Mapping)映射机制将Java...
首先,Repository 是Spring Data JPA的核心组件,它为数据访问提供了一种声明式的方法。Repository接口层级包括Repository、CrudRepository、PagingAndSortingRepository 和JpaRepository。Repository是所有仓库接口...
1:JPA: : 2:对象数据库: ://www.objectdb.com/ 3:JPA 性能基准: ://www.jpab.org/All/All/All.html 支持功能 支持所有蓝图功能, 除外 支撑 支持 支持 Java 5、6 或 7 支持 JPA 你需要哪一个取决于你想用...
3. **Spring Data JPA**:对于基于Java Persistence API (JPA)的应用,Spring Data JPA提供了一种声明式的方式来进行数据访问,开发者只需定义Repository接口,Spring Data JPA就能自动生成实现,包含CRUD方法和其他...
2. **JPA(Java Persistence API)**:JPA是Java EE规范的一部分,用于管理关系数据库的对象-关系映射(ORM)。它提供了一种标准API,开发者可以使用注解或XML定义实体类与数据库表之间的映射。 3. **Spring Data ...
春天数据jpa额外使用jpa的spring数据更舒适我爱spring-data-jpa,她放开我的双手,粗鲁的方法很无聊! 然而,尽管她为我们提供了规范解决方案,但她在动态本机查询上并不完美,而且她的返回类型必须是一个实体,但是...
使用JPA访问数据: : 。 使用REST访问JPA数据: : 。项目中的Bean全部采用进行精简,需要配合IDE插件使用,在此项目不进行讨论,如需了解更多,参考以下链接:官方文档地址: : 。官方下载地址: : 。 lombok-...
这对于那些需要确保实体对象数据是最新的场景非常有用。 5. **使用`clear()`方法** `clear()`方法会清除实体管理器当前上下文中所有的托管实体对象,使它们变为游离状态。需要注意的是,未及时提交到数据库的更改...
在本篇“Spring基础:数据访问(2)”中,我们将深入探讨Spring框架如何支持和管理数据访问,特别是在Java应用程序中的数据库交互。Spring以其强大的依赖注入(Dependency Injection,DI)和面向切面编程(Aspect-...
【标题】"notes_JPA_JSF:使用JSF和JPA实施来重建Notes项目"涉及的是在Java开发...通过学习和分析这个项目,你可以深入理解JSF的事件处理机制、组件使用,以及JPA的实体管理、数据访问策略,提升你的Java EE开发能力。
"spring3+springmvc+jpa+hibernate多数据源"是一个示例项目,它演示了如何在一个应用中集成Spring 3、Spring MVC、JPA 2.0以及Hibernate,以实现对多个数据源的支持。下面将详细介绍这些技术及其集成的关键点。 **...
在Spring Boot应用中,使用`spring-data-jpa`来配置MySQL多数据源是一项常见的需求,尤其是在构建大型分布式系统时,为了实现数据隔离、负载均衡或读写分离等目的。本教程将详细介绍如何在Spring Boot项目中配置多个...
ORM 映射元数据:JPA 支持 XML 和 JDK 5.0 注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。 JPA 的 API:用来操作实体对象,执行CRUD操作,框架在后台完成所有...
学习和掌握JPA对于开发Java企业级应用至关重要,因为它简化了数据库操作,使得开发者可以更专注于业务逻辑,而不是底层的数据访问细节。同时,了解JPA还可以帮助你更好地理解和使用其他ORM框架,如Hibernate和...