解析资源库架构模式

无论应用程序的设计方法和实现技术如何发展,数据访问仍然是任何系统都需要考虑的基础技术问题。针对数据访问过程,我们可以理解为任何一个系统都应该存在这样一个起点,我们可以从这个起点遍历到具体的数据。换句话说,系统中应该存在一个数据提供者,通过这个数据提供者就能获取到所有想要的数据对象。而我们今天要介绍的资源库本质上就是这样一种数据的提供者。


关于如何更好的设计上图中的资源库,业界也诞生了一些主流的设计方法。这些设计方法解决的核心问题有两个,即:

  1. 如何实现应用程序的内存数据和具体持久化数据之间的转换关系?
  2. 如何在应用程序中抽离独立的数据持久化访问入口?

第一个问题的解决思路比较简单,一般做法是实现一个数据映射层。第二个问题是第一个问题的延伸,我们通过资源库来提供统一的数据访问入口。下图展示了资源库的基本结构,从中可以看出内存数据与持久化数据的映射关系,以及资源库与持久化媒介之间的交互过程。


在架构设计上,资源库是一种常见的架构模式,应用非常广泛,我们经常使用的Hibernate、Mybatis等各种ORM框架实际上都是这一架构模式的具体实现框架。今天,我们就将围绕资源库架构模式的基本概念以及具体应用场景展开详细讨论。

资源库架构模式 

简单来说,资源库作为对象的提供者,指的就是能够实现对象的持久化。持久化的媒介有很多,传统的关系型数据库、各种NoSQL以及代表持久化新方向的NewSQL都可以进行数据的CRUD操作。显然,针对这些不同持久化媒介的具体技术体系是不一样的。


为了通过资源库来屏蔽数据访问的技术差异性和复杂性,我们需要设计资源库的具体表现形式。下图展示了其中的一种表现形式。


在上图中,客户端通过一定的查询条件来对资源库发起请求,然后资源库把数据访问的具体技术和实现策略封装起来。这样,开发人员通过资源库就可以实现各种类型的数据访问操作。

在这些数据访问操作中,主要的应用场景是数据查询。因此,对查询类操作的支持程度也是衡量一个资源库实现方案的重要标准。通常,开发人员可以通过构建不同的规约化查询对象来对资源库发起请求。下图展示这一应用场景下的类图结构。


在上图中,基于Account这个业务对象,我们把这两个规约化查询对象命名为AccountSpecificationByUserName和AccountSpecificationByAgeRange。而这里的Repository和Specification接口分别代表资源库和查询对象。

看了这张图,你可以会觉得困惑,我们如何实现图中的各个技术组件的?不用担心,业界已经诞生了一些资源库架构模式的实现框架,其中最具代表性的就是Spring家族的Spring Data框架。让我们一起来看一下。

Spring Data中的资源库模式

Spring Data是Spring家族中专门用于实现数据访问的开源框架,其核心理念是支持对所有持久化媒介进行资源化配置从而实现数据访问。我们知道,数据访问需要完成业务对象与持久化数据之间的映射并对外提供访问入口,Spring Data基于资源库架构模式抽象出一套实现该模式的统一数据访问方式。

Spring Data和资源库

我们先来看Spring Data中用来表示资源库的Repository接口。Repository接口是Spring Data中对数据访问的最高层抽象,接口定义如下所示。

public interface Repository<T, ID> {

}

我们看到Repository接口只是一个空接口,通过泛型指定了业务实体对象的类型和ID。在Spring Data中,存在一大批Repository接口的子接口和实现类,该接口的部分类层结构如下所示。


可以看到CrudRepository 接口是对Repository接口的最常见扩展,添加了业务对象的CRUD操作功能,定义如下。

public interface CrudRepository<T, ID> extends Repository<T, ID> {

  //保存单个实体对象

<S extends T> S save(S entity);

//保存一组实体对象

<S extends T> Iterable<S> saveAll(Iterable<S> entities);

//根据Id查询单个实体对象

Optional<T> findById(ID id);

//判断指定Id的对象是否存在

boolean existsById(ID id);

//获取所有实体对象

Iterable<T> findAll();

  //根据一组Id获取对应的一组实体对象

Iterable<T> findAllById(Iterable<ID> ids);

//获取对象总数

long count();

//根据Id删除实体对象

void deleteById(ID id);

//根据实体对象执行删除操作

void delete(T entity);

  //删除一组实体对象

void deleteAll(Iterable<? extends T> entities);

//删除所有实体对象

void deleteAll();

}

这些方法都是自解释的,我们可以看到CrudRepository接口提供了一组非常实用的数据访问操作。

在Spring Data的官方网站中列出来其所提供的所有组件,包括针对关系型数据库的JPA 资源库,也包括针对MongoDB、Neo4j、Redis等各种NoSQL对应的资源库等。


在接下来的内容中,我们重点讨论的是用于实现关系型数据库访问的Spring Data JPA,在该组件中存在一个JpaRepository接口,该接口是CrudRepository的子接口。在日常开发过程中,我们通常可以借助于方法名衍生查询功能来使用JpaRepository接口。

所谓的方法名衍生查询是Spring Data的查询特色之一,通过在方法命名上直接使用查询字段和参数,资源库就能自动识别相应的查询条件并组装对应的查询语句。典型的示例如下所示。

public interface AccountRepository extends JpaRepository<Account,

Long> {

List<Account> findByFirstNameAndLastName(String firstName, String

lastName);

}

在上面的例子中,通过findByFirstNameAndLastname这样符合普通语义的方法名,并在参数列表中按照方法名中参数的顺序和名称传入相应的参数,即第一个参数是fistName,第二个参数lastName,Spring Data就能自动组装SQL语句从而实现衍生查询。是不是很神奇?

Specification机制

介绍完JpaRepository的基本用法之后,我们再来介绍和资源库架构模式密切相关的一种规约化查询机制,即Specification机制。考虑这样一种场景,我们需要动态的根据不同条件来对某一个对象执行查询操作,那么就可以引入Spring Data JPA中的JpaSpecificationExecutor接口来实现这一目标。

你可能会问,为什么我们需要使用Specification机制呢?核心原因是该机制具备的类型安全性。我们同样来看一个示例,假设我们有一个继承了JpaSpecificationExecutor的OrderJpaRepository,定义如下。

public interface OrderJpaRepository extends JpaRepository<Order, Long>, JpaSpecificationExecutor<Order>{

对于JpaSpecificationExecutor接口而言,背后使用的就是Specification接口,该接口的核心方法就一个,如下所示。

Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);

通过这里的toPredicate方法,我们就可以根据root中所传入的业务对象,以及CriteriaQuery和CriteriaBuilder中的查询条件来实现动态化查询。

基于Specification机制,我们也来给出一个典型的实现方法,如下所示。

public Order getOrderByOrderNumberBySpecification(String orderNumber) {

Order order = new Order();

order.setOrderNumber(orderNumber);

Specification<Order> spec = new Specification<Order>() {

            @Override

            public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

                Path<Object> orderNumberPath = root.get("orderNumber");

              

                Predicate predicate = cb.equal(orderNumberPath, orderNumber);

                return predicate;

            }

        };

return orderJpaRepository.findOne(spec).orElse(new Order());

}

可以看到,在这里的toPredicate方法中,我们从root对象中获取了“orderNumber”属性,然后通过cb.equal方法将该属性与传入的“orderNumber”参数进行比对,从而完成查询条件的构建。这种实现方式是资源库使用过程中的常见做法。

数据访问是一切应用系统的基础,而资源库架构模式为我们实现数据访问提供了设计思想和方法。架构模式的落地需要具体的开发框架。基于资源库架构模式,Spring Data为开发人员提供了一系列用于完成CRUD操作的工具方法,尤其是针对最常用的查询操作更是专门进行了提炼和设计使得开发过程变得简单而高效。在今天的内容中,我们基于资源库架构模式以及Spring Data框架给出了数据访问过程中的一些代码示例,这些代码示例都可以直接应用到日常开发过程中。

  • 7
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值