使用JPA实现Specification规范规格模式

由DDD之父 Eric Evans 和OO之父 Martin Fowler定义的规范(Specification也称规格模式)模式article 越来越受到广泛应用,本文介绍如何使用JavaEE 持久层规范JPA实现规格模式,其实现思想也适合其他持久层框架。案例源码见GitHub。

在这个文章中,我们将使用以下POLL类作为创建Specification为例实体。它代表了民意调查,有一个开始和结束日期。在这两个日期之间的时间内用户可以在不同的选择之间投票表决Poll,投票Poll可以在管理员的结束日期尚未到达前锁定。在这种情况下,一个锁日期将被设置。

@Entity

public class Poll {

@Id

@GeneratedValue

private long id;

private DateTime startDate;

private DateTime endDate;

private DateTime lockDate;

@OneToMany(cascade = CascadeType.ALL)

private List votes = new ArrayList<>();

}

现在我们有两个约束:

一个投票poll如果满足startDate < now < endDate,那表示它在进行中。

一个投票poll如果超过100个投票但是没有锁定,表示它很流行。

我们可以通过在POLL投票添加适当的方法如:poll.isCurrentlyRunning(),另外,我们可以使用一个服务方法pollService.isCurrentlyRunning(进行轮询)。然而,我们也希望能够查询数据库获得所有当前正在运行的民意调查。因此,我们可以添加一个DAO或储存repository库的方法类似pollRepository.findAllCurrentlyRunningPolls()

如果我们想结合上述两个约束查询,例如我们想在数据库中查询当前正在运行的所有流行的调查名单?这时Specification模式派上用场。当使用Specification模式时,我们将业务规则转换为额外的类称为Specification。

创建Specification接口和抽象类如下:

public interface Specification {

boolean isSatisfiedBy(T t);

Predicate toPredicate(Root root, CriteriaBuilder cb);

Class getType();

}

abstract public class AbstractSpecification implements Specification {

@Override

public boolean isSatisfiedBy(T t) {

throw new NotImplementedException();

}

@Override

public Predicate toPredicate(Root poll, CriteriaBuilder cb) {

throw new NotImplementedException();

}

@Override

public Class getType() {

ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();

return (Class) type.getActualTypeArguments()[0];

}

}

specification的核心是isSatisfiedBy() 方法,用于检查一个对象是否满足规范规格要求。. toPredicate()是一个附加的方法,我们在案例中使用它返回一个约束 javax.persistence.criteria.Predicate 实例,它能被用于查询一个数据库。

检查一个poll是否正在运行的规范实现如下:

public class IsCurrentlyRunning extends AbstractSpecification {

@Override

public boolean isSatisfiedBy(Poll poll) {

return poll.getStartDate().isBeforeNow()

&& poll.getEndDate().isAfterNow()

&& poll.getLockDate() == null;

}

@Override

public Predicate toPredicate(Root poll, CriteriaBuilder cb) {

DateTime now = new DateTime();

return cb.and(

cb.lessThan(poll.get(Poll_.startDate), now),

cb.greaterThan(poll.get(Poll_.endDate), now),

cb.isNull(poll.get(Poll_.lockDate))

);

}

}

检查一个投票是否流行的代码如下:

public class IsPopular extends AbstractSpecification {

@Override

public boolean isSatisfiedBy(Poll poll) {

return poll.getLockDate() == null && poll.getVotes().size() > 100;

}

@Override

public Predicate toPredicate(Root poll, CriteriaBuilder cb) {

return cb.and(

cb.isNull(poll.get(Poll_.lockDate)),

cb.greaterThan(cb.size(poll.get(Poll_.votes)), 5)

);

}

}

使用代码:

boolean isPopular = new IsPopular().isSatisfiedBy(poll);

boolean isCurrentlyRunning = new IsCurrentlyRunning().isSatisfiedBy(poll);

为了查询数据库,我们需要继承扩展DAO/Repository来支持规范:

public class PollRepository {

private EntityManager entityManager = ...

public List findAllBySpecification(Specification specification) {

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

// use specification.getType() to create a Root instance

CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(specification.getType());

Root root = criteriaQuery.from(specification.getType());

// get predicate from specification

Predicate predicate = specification.toPredicate(root, criteriaBuilder);

// set predicate and execute query

criteriaQuery.where(predicate);

return entityManager.createQuery(criteriaQuery).getResultList();

}

}

我们使用 AbstractSpecification的getType() 方法 创建CriteriaQuery 和 Root 实例. getType()返回的是一个 AbstractSpecification泛型,可以由子类定义. 对于流行IsPopular 且正在进行IsCurrentlyRunning它返回一个Poll class. 如果没有 getType()我们必须在每个规范的 toPredicate() 中实现创建 CriteriaQuery 和 Root实例。它只是减少烦人代码的帮助者。

使用代码:

List popularPolls = pollRepository.findAllBySpecification(new IsPopular());

List currentlyRunningPolls = pollRepository.findAllBySpecification(new IsCurrentlyRunning());

下面问题是如何结合这两个规范,我们如何查询数据库所有流行且还在进行的投票呢?

使用组合模式实现组合规范模式。名称为AndSpecification:

public class AndSpecification extends AbstractSpecification {

private Specification first;

private Specification second;

public AndSpecification(Specification first, Specification second) {

this.first = first;

this.second = second;

}

@Override

public boolean isSatisfiedBy(T t) {

return first.isSatisfiedBy(t) && second.isSatisfiedBy(t);

}

@Override

public Predicate toPredicate(Root root, CriteriaBuilder cb) {

return cb.and(

first.toPredicate(root, cb),

second.toPredicate(root, cb)

);

}

@Override

public Class getType() {

return first.getType();

}

}

使用代码:

Specification popularAndRunning = new AndSpecification<>(new IsPopular(), new IsCurrentlyRunning());

List polls = myRepository.findAllBySpecification(popularAndRunning);

为了提供可读性,我们增加and方法到规范接口

public interface Specification {

Specification and(Specification other);

// other methods

}

abstract public class AbstractSpecification implements Specification {

@Override

public Specification and(Specification other) {

return new AndSpecification<>(this, other);

}

// other methods

}

使用代码:

Specification popularAndRunning = new IsPopular().and(new IsCurrentlyRunning());

boolean isPopularAndRunning = popularAndRunning.isSatisfiedBy(poll);

List polls = myRepository.findAllBySpecification(popularAndRunning);

specification java_使用JPA实现Specification规范模式 -解道Jdon相关推荐

  1. java mediator模式_设计模式之Mediator 中介者模式 - 解道Jdon

    设计模式目录 >> 当前页 设计模式之Mediator(中介者) 板桥里人 http://www.jdon.com 2002/05/05 Mediator中介者模式定义: 用一个中介对象来 ...

  2. java jpa 规范_Java:在JPA中使用规范模式

    java jpa 规范 本文是在Java中使用规范模式的简介. 我们还将看到如何将经典规范与JPA Criteria查询结合使用,以从关系数据库中检索对象. 在本文中,我们将使用以下Poll类作为创建 ...

  3. Java:在JPA中使用规范模式

    本文是在Java中使用规范模式的简介. 我们还将看到如何将经典规范与JPA Criteria查询结合使用,以从关系数据库中检索对象. 在本文中,我们将使用以下Poll类作为创建规范的示例实体. 它表示 ...

  4. spring data jpa封装specification实现简单风格的动态查询

    github:https://github.com/peterowang/spring-data-jpa-demo 单一实体的动态查询: @Servicepublic class AdvancedUs ...

  5. SpringBoot JPA使用Specification多表查询LEFT JOIN

    1.Student package com.frank.jpaSpecification.entity;import lombok.AllArgsConstructor; import lombok. ...

  6. php设计要求,《PHP设计模式介绍》第十章 规范模式

    在一个应用软件的成型过程中,一些意想不到的商业逻辑到处出现.比如,基于价格的考虑,这个任务必须减少项目:而那个任务也因为销售税而必须选择合适的比率:而其它的任务也必须因为其他的特别条件而终止.一些商业 ...

  7. USB Type C规范详解

    USB Type C规范详解 目前USB Type C接口应用非常广泛,可以传输DP,USB,PCIE,音频等信号,已经不是纯粹的用来传输USB信号了,即USB Type C摆脱了和USB的从属关系, ...

  8. java 注释 超链接_java_Java代码注释规范详解,代码附有注释对程序开发者来 - phpStudy...

    Java代码注释规范详解 代码附有注释对程序开发者来说非常重要,随着技术的发展,在项目开发过程中,必须要求程序员写好代码注释,这样有利于代码后续的编写和使用. 基本的要求: 1.注释形式统一 在整个应 ...

  9. php psr2规范,php标准规范详解

    本文主要和大家分享php标准规范详解,希望能帮助到大家. psr0:自动加载标准已经被 psr4 替代,可以了解下 1. 强制:完全限定命名空间和类的格式:\\(\)* 2. 强制:每个命名空间必须有 ...

  10. 整合经营模式之道(序)——暨“一路一起舞吧”开博之作

    整合经营模式之道(序)                                 暨"一路一起舞吧"开博之作 --经营狂士 非常高兴,构思好几天的文章终于落笔.本人将携独创的 ...

最新文章

  1. bzoj2243 [SDOI2011]染色
  2. Hibernate commit() 和flush() 的区别
  3. Linux下的文件共享全攻略系列之一:Samba服务器简介与快速配置指南
  4. 页面加载图片前用空态图代替真正图片
  5. SharedPreferences 的使用,commit和apply两个方法的区别
  6. Linux计划任务(at,crontab)
  7. IdHTTP处理HTTP 302遇到的问题
  8. Nginx 配置一个虚拟站点
  9. JavaScript使用button提交表单
  10. Linux NAT基本流程与实现技巧
  11. matlab fread每隔,matlab 中关于fread函数的用法
  12. 新装电脑能装w ndows7吗,为什么刚买的新电脑,却不支持安装Win7系统,背后的真实原因?...
  13. 英皇考级——听力测试的训练方法
  14. 怎样写一篇critical review
  15. 推荐几个免费的在线文本转语音网站(支持中英文多种语音)
  16. EDA技术与应用上机任务 电子信息类 Quartus II或Quartus Prime D触发器、半减器、全减器、可加减控制的50进制加减计数器。
  17. Oracle日期型函数详解
  18. 163邮箱怎么申请注册?邮箱收费版怎么收费?163邮箱收费版优势?
  19. 照着别人的敲代码来学习编程好吗
  20. 深度盘点:机器学习、深度学习面试知识点3W字汇总

热门文章

  1. 学车科目三考试视频讲解实地考场陪练车包最新经验分享必过攻略
  2. VMware Workstation创建Windows 8.1虚拟机
  3. 2022电大国家开放大学网上形考任务-大学语文非免费(非答案)
  4. 报刊订阅管理系统的设计与实现
  5. 计算机小高考成绩,2018江苏小高考成绩出来了!昆山*亮眼的学校是…
  6. 计算器存储功能怎么用_数控车床加工刀具补偿功能怎么用?
  7. leetcode刷题java之739. 每日温度
  8. Openpose官方编译及其训练模型
  9. 解决kafka传输超大图片消费者接收失败问题
  10. Cesium添加百度地图