制作手机端网站,北京建站,如何做推广链接,建外做网站的公司使用JPA实现Specification规范规格模式由DDD之父 Eric Evans 和OO之父 Martin Fowler定义的规范(Specification也称规格模式)模式article 越来越受到广泛应用#xff0c;本文介绍如何使用JavaEE 持久层规范JPA实现规格模式#xff0c;其实现思想也适合其他持久层框架。案例源…使用JPA实现Specification规范规格模式由DDD之父 Eric Evans 和OO之父 Martin Fowler定义的规范(Specification也称规格模式)模式article 越来越受到广泛应用本文介绍如何使用JavaEE 持久层规范JPA实现规格模式其实现思想也适合其他持久层框架。案例源码见GitHub。在这个文章中我们将使用以下POLL类作为创建Specification为例实体。它代表了民意调查有一个开始和结束日期。在这两个日期之间的时间内用户可以在不同的选择之间投票表决Poll投票Poll可以在管理员的结束日期尚未到达前锁定。在这种情况下一个锁日期将被设置。Entitypublic class Poll {IdGeneratedValueprivate 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 {Overridepublic boolean isSatisfiedBy(T t) {throw new NotImplementedException();}Overridepublic Predicate toPredicate(Root poll, CriteriaBuilder cb) {throw new NotImplementedException();}Overridepublic 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 {Overridepublic boolean isSatisfiedBy(Poll poll) {return poll.getStartDate().isBeforeNow() poll.getEndDate().isAfterNow() poll.getLockDate() null;}Overridepublic 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 {Overridepublic boolean isSatisfiedBy(Poll poll) {return poll.getLockDate() null poll.getVotes().size() 100;}Overridepublic 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 instanceCriteriaQuery criteriaQuery criteriaBuilder.createQuery(specification.getType());Root root criteriaQuery.from(specification.getType());// get predicate from specificationPredicate predicate specification.toPredicate(root, criteriaBuilder);// set predicate and execute querycriteriaQuery.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());下面问题是如何结合这两个规范我们如何查询数据库所有流行且还在进行的投票呢使用组合模式实现组合规范模式。名称为AndSpecificationpublic class AndSpecification extends AbstractSpecification {private Specification first;private Specification second;public AndSpecification(Specification first, Specification second) {this.first first;this.second second;}Overridepublic boolean isSatisfiedBy(T t) {return first.isSatisfiedBy(t) second.isSatisfiedBy(t);}Overridepublic Predicate toPredicate(Root root, CriteriaBuilder cb) {return cb.and(first.toPredicate(root, cb),second.toPredicate(root, cb));}Overridepublic 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 {Overridepublic 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);