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

使用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);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/431535.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

sublime用cmd窗口调试python_如何使用xdebug和sublime调试python脚本

然后我去了XDebug.崇高-设置&#xff0c;但我不确定如何配置它(我链接到什么&#xff0c;它需要什么网址&#xff0c;等等…){// For remote debugging to resolve the file locations// it is required to configure the path mapping// with the server path as key and loca…

Tomcat免安装版的环境变量配置以及Eclipse下的Tomcat配置和测试

Tomcat是目前比较流行的开源且免费的Web应用服务器&#xff0c;在我的电脑上第一次安装Tomcat&#xff0c;再经过网上教程和自己的摸索后&#xff0c;将这个过程 重新记录下来&#xff0c;以便以后如果忘记了可以随时查看。 注意&#xff1a;首先要明确一点&#xff0c;Tomcat与…

java开发和structs的关系_java---springMVC与strutsMVC的区别

项目刚刚换了web层框架&#xff0c;放弃了struts2改用spring3mvc当初还框架的时候目的比较单纯---springmvc支持rest&#xff0c;小生对restful url由衷的喜欢不用不知道 一用就发现开发效率确实比struts2高我们用struts2时采用的传统的配置文件的方式&#xff0c;并没有使用传…

python unicode error_关于GAE中运行python出现unicode decode error

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼老问题了&#xff0c;但因为本人不会python语法但却要用&#xff0c;找了很久解决办法&#xff0c;比如添加一行# -*- coding: utf-8 -*-但还是不行。。主要errorUnicodeDecodeError: *ascii* codec can*t decode byte 0xb0 in pos…

【推荐】介绍两款Windows资源管理器,Q-Dir 与 FreeCommander XE(比TotalCommander更易用的免费资源管理器)...

你是否也像我一样&#xff0c;随着硬盘、文件数量的增加&#xff0c;而感到对于文件的管理越来越乏力。 于是我试用了传说中的各种软件&#xff0c;包括各种Explorer外壳&#xff0c;或者第三方资源管理器。 最后我确定下来经常使用&#xff0c;并推荐给您的是这两款软件&#…

java rpg项目代码_java rpg游戏代码(移动保存读取)

package ggg;import java.awt.*;import java.awt.event.*;import javax.swing.*;import java.util.*;import java.io.*;public class Tank1 extends JFrame{//用来存储对应的图片的二维数组 (这里的icon数组&#xff0c;只是用来将我们设置好的数组在界面上显示出来&#xff0…

java类加载器_java底层内功 第一章,类加载器的任性

java类是怎么加载的&#xff1f;类加载机制JVM主要包含三大核心部分&#xff1a;类加载器&#xff0c;运行时数据区和执行引擎。虚拟机将描述类的数据从class文件加载到内存&#xff0c;并对数据进行校验&#xff0c;准备&#xff0c;解析和初始化&#xff0c;最终就会形成可以…

Java学习二:Javac Java的学习(原创)

安装完了JDK&#xff0c;就可以编译、执行简答的Java程序了&#xff0c; 一、Javac ,Java 路径名和包名的关系&#xff1a; 在D盘下&#xff0c;建文件夹Test&#xff0c;在Test里创建文件Java1.java(类名要与文件名一致&#xff09; Java1.java : package Test; public c…

java的requestmapping_SpringMVC RequestMapping 详解

SpringMVC RequestMapping 详解RequestMapping这个注解在SpringMVC扮演着非常重要的角色&#xff0c;可以说是随处可见。它的知识点很简单。今天我们就一起学习SpringMVC的RequestMapping这个注解。文章主要分为两个部分&#xff1a;RequestMapping 基础用法和RequestMapping 提…

redis查询所有key命令_想在生产搞事情?那试试这些 Redis 命令

作者&#xff1a;鸭血粉丝出自&#xff1a;Java极客技术原文&#xff1a;mp.weixin.qq.com/s/WeAamgYYGQfxlsppsn9_lg哎&#xff0c;最近阿粉又双叒叕犯事了。事情是这样的&#xff0c;前一段时间阿粉公司生产交易偶发报错&#xff0c;一番排查下来最终原因是因为 Redis 命令执…

PrintArea打印,@media screen解决移动web开发的多分辨率问题,@media print设置打印的样式...

PrintArea打印&#xff0c;局部DIV打印插件&#xff0c;依赖JQuery。 github:https://github.com/RitsC/PrintArea 当打印时需要临时改变页面布局&#xff0c;可以使用 media print{ /* * CSS */ } 打印时生效&#xff0c;打印完自动失效。 需要屏幕自适应&#xff0c;或多种分…

java arraycopyof_Java中System.arraycopy()和Arrays.copyOf()的区别

System.arraycopy()这是一个由java标准库提供的方法。用它进行复制数组比用for循环要快的多。arraycopy()需要的参数有&#xff1a;源数组&#xff0c;从源数组中的什么位置开始复制的偏移量&#xff0c;目标数组&#xff0c;从目标数组中的什么位置开始复制的偏移量&#xff0…

python 异步 生产者 消费者_python 线程通信 生产者与消费者

1 """2 线程通信的生产者与消费者3 python的queue模块中提供了同步的线程安全的队列类,都具有原子性&#xff0c;实现线程间的同步4 Queue (FIFO&#xff1a; fist in fist out)5 LifoQueue (LIFO&#xff1a; last in fist out)6 PriorityQueue (优先级队列)78…

3、C#面向对象:封装、继承、多态、String、集合、文件(下)

面向对象多态 一、装箱和拆箱 装箱&#xff1a;将值类型转换为引用类型。object o 1&#xff1b;值类型给引用类型赋值 拆箱&#xff1a;将引用类型转换为值类型。int n (int)o; 强制转换为值类型 满足条件&#xff1a;两种类型是否存在继承关系。 int n Convert.ToInt32(&q…

mysql gui 分区_一文彻底搞懂MySQL分区

一.InnoDB逻辑存储结构首先要先介绍一下InnoDB逻辑存储结构和区的概念&#xff0c;它的所有数据都被逻辑地存放在表空间&#xff0c;表空间又由段&#xff0c;区&#xff0c;页组成。段段就是上图的segment区域&#xff0c;常见的段有数据段、索引段、回滚段等&#xff0c;在In…

js 获取域名_RapidDNS域名查询如何联动Goby

前言&#xff1a;http://RapidDNS.io 是一个秒级在线子域名和同IP域名的查询工具。目前拥有25亿条DNS记录&#xff0c;支持A、AAAA、CNAME、MX4种DNS记录类型。由于Goby程序对子域名收集方面不是很完善&#xff0c;这里特编写此插件作为其拓展。可以方便快速获取域名和ip地址信…

iOS 9应用开发教程之iOS 9新特性

iOS 9应用开发教程之iOS 9新特性 iOS 9开发概述 iOS 9是目前苹果公司用于苹果手机和苹果平板电脑的最新的操作系统。该操作系统于2015年6月8号&#xff08;美国时间&#xff09;被发布。本章将主要讲解iOS 9的新特性、以及使用Xcode 7.0如何编写一个简单的iOS 9的应用程序等内容…

python后端框架flask_Vue+Flask轻量级前端、后端框架,如何完美同步开发

导言我们的Vue2.0应用&#xff1a;简读-微信公众号RSS&#xff0c;即将进入后端开发。VueFlask作为轻量级前端、后端框架&#xff0c;非常适合组合起来快速开发。一个是js&#xff0c;一个是Python。Bonus:可以完美实现跨域调试&#xff0c;不需要JSONP&#xff0c;也不需要服务…

mysql查询错误_一个奇怪的MySQL查询错误

t_user表的phone_number字段是varchar(255)类型的&#xff0c;表示手机号&#xff0c;在查询某个手机号时&#xff0c;sql语句如下&#xff1a;SELECT phone_number FROM t_user WHERE phone_number 13400000000查询结果&#xff1a;phone_number---------------------------…

hdu 3308 线段树

输入一串数字&#xff0c;有两个操作&#xff1a;Q a b 查询a到b区间内严格递增子串的最大长度 &#xff1b; U a b 把第a位数字替换成b 。注意输入的编号是从0开始 解法&#xff1a;线段树维护区间的严格递增子串的最大长度即可。注意细节。 #include <iostream> #inclu…