存储库模式的好处和 Spring 实施

Repository pattern benefits and Spring implementation

在文章 Don’t use DAO, use Repository 中,很好地解释了 DAO 和存储库模式之间的区别。

我的简短复述 - DAO 使我们使用多种方法膨胀接口,这阻碍了更改和测试。反过来,存储库使用 query 方法封装所有 customizations/changes,该方法接受 Specification 作为参数。当您在存储库中需要新行为时 - 您不应该更改它,而是创建 Specification 的新继承人。

我的结论 - 存储库模式优于 DAO,因为它的接口不允许修改。

到目前为止我说得对吗?我没有错过存储库模式的一些好处吗?

但是,如果您查看 Spring's accessing data JPA guide,您会发现下一个代码:

public interface CustomerRepository extends CrudRepository<Customer, Long> {
    List<Customer> findByLastName(String lastName); //each time you need custom behavior you need to add a method
    //...
}

不会和上一篇冲突吗?为什么 Spring 的存储库强制我们向​​接口添加新方法?

My conslusion - repository pattern is better than DAO due its interfaces are closed to modification

这取决于...
因为存储库模式更复杂,因为它需要编写更多的代码,并且对于存储库的客户端和它的实现具有比 DAO 模式更高的抽象级别。
当您需要在查询中具有灵活性时 and/or 您的查询在结果中混合了多个实体,存储库模式可以满足这些需求。 如果您主要需要对 table(基本的创建、读取、更新和删除操作)进行简单的 crud 操作,我认为使用真实的存储库(因此具有规范)可能会产生开销。

BUT, if you'd look at Spring's accessing data JPA guide, you'll find the next code:

 public interface CustomerRepository extends CrudRepository<Customer,Long> {
    List<Customer> findByLastName(String lastName); //each time you need custom behavior you need to add a method
     //... }

Doesn't it conflict with the previous article? Why Spring's repository force us to add new methods to interface?

是的。我认为问题来自 Spring,它使用时尚术语(存储库)来代表 class,根据模式文献,它不是存储库。 (http://martinfowler.com/eaaCatalog/repository.html)
我认为您应该将 Spring Repository 视为 DAO,因为 Spring repository 的基本功能接口是 CrudRepository。从本质上讲,CRUD 操作是我们在 DAO 中找到的操作...

之后,没有什么能阻止您丰富存储库以提供具有Spring规范的存储库方法,如Spring data官方文档的2.4点。
Spring 建议您像示例中那样扩展 org.springframework.data.repository.CrudRepository 接口。这是更简单的做事方式,我想这是最常见的做事方式...

public interface CustomerRepository extends CrudRepository<Customer, Long>, JpaSpecificationExecutor {
 …
}

并且JpaSpecificationExecutor提供了使用规范的方法,因此促进了查询源代码中重复处理的减少:

/*
 * Copyright 2008-2011 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.springframework.data.jpa.repository;

import java.util.List;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

/**
 * Interface to allow execution of {@link Specification}s based on the JPA criteria API.
 * 
 * @author Oliver Gierke
 */
public interface JpaSpecificationExecutor<T> {

    /**
     * Returns a single entity matching the given {@link Specification}.
     * 
     * @param spec
     * @return
     */
    T findOne(Specification<T> spec);

    /**
     * Returns all entities matching the given {@link Specification}.
     * 
     * @param spec
     * @return
     */
    List<T> findAll(Specification<T> spec);

    /**
     * Returns a {@link Page} of entities matching the given {@link Specification}.
     * 
     * @param spec
     * @param pageable
     * @return
     */
    Page<T> findAll(Specification<T> spec, Pageable pageable);

    /**
     * Returns all entities matching the given {@link Specification} and {@link Sort}.
     * 
     * @param spec
     * @param sort
     * @return
     */
    List<T> findAll(Specification<T> spec, Sort sort);

    /**
     * Returns the number of instances that the given {@link Specification} will return.
     * 
     * @param spec the {@link Specification} to count instances for
     * @return the number of instances
     */
    long count(Specification<T> spec);
}

但我认为它会创造科学怪人生物:一半是 DAO,一半是存储库。

另一个解决方案是直接实现基本和标记接口:org.springframework.data.repository.RepositoryJpaSpecificationExecutor 类似的接口:

public interface CustomerRepository extends Repository<Customer, Long>, JpaSpecificationExecutor {
 …
}

通过这种方式,您可以拥有一个真正的存储库,只有规范方法。
我不知道它是否有效。我从没试过。

编辑:回复评论

存储库模式是 Hibernate 不提供现成解决方案的概念。
但是 Hibernate 和更普遍的 JPA 2 规范确实提供了 Criteria 作为创建规范的基本要素 classes。
然后,您可以创建一个自定义的 class,它通过提出以规范作为输入的所需方法来实现存储库模式。 我认为您可以使用 ORM 和存储库,因为您可以通过使用标准 API 将 ORM 与规范一起使用。 有些人反对 ORM 和 Repository,我不同意。 ORM 不是基于 DAO 模式。 DAO 是操作数据库中数据的方式。 ORM 是构造数据对象以表示数据库结构的方法。 例如,通过同时使用两者,您可以从规范的灵活性和关系对象映射以及实体之间编织的强大功能中获益。
如果你不像 IBatis 那样使用 ORM 或 ORM,你应该自己编写 ORM 提供给你的东西:对象关系映射。