【问题标题】:Spring Data: multiple repository interfaces into a single 'repository' service classSpring Data:将多个存储库接口集成到一个“存储库”服务类中
【发布时间】:2016-11-24 07:44:09
【问题描述】:

由于数据库的设计,我有相当多的JpaRepository 扩展存储库接口。

为了构造一个简单的对象,即Person,我必须对大约 4 - 5 个存储库进行方法调用,因为数据在整个数据库中都是这样分布的。像这样的东西(请原谅伪代码):

@Service
public class PersonConstructService {

    public PersonConstructService(Repository repository,
                                  RepositoryTwo repositoryTwo,
                                  RepositoryThree repositoryThree) {

        public Person constructPerson() {
            person
                .add(GetDataFromRepositoryOne())
                .add(GetDataFromRepositoryTwo())
                .add(GetDataFromRepositoryThree());

            return person;
        }

        private SomeDataTypeReturnedOne GetDataFromRepositoryOne() {
            repository.doSomething();
        }

        private SomeDataTypeReturnedTwo GetDataFromRepositoryTwo() {
            repositoryTwo.doSomething();
        }

        private SomeDataTypeReturnedThree GetDataFromRepositoryThree() {
            repositoryThree.doSomething();
        }
    }
}

PersonConstructService 类使用所有这些接口只是为了构造一个简单的Person 对象。我从PersonConstructService 类中的不同方法调用这些存储库。我曾想过将这个类分散到多个类中,但我认为这是不正确的。

相反,我想使用repositoryService,其中包括创建Person 对象所需的所有存储库。这是一个好方法吗?春天可以吗?

我问的原因是有时注入服务的数量约为 7-8。这绝对不好。

【问题讨论】:

  • 使用 Spring,您可以首先使用 @Autowired 批注让您的服务准备好在您的课程中使用,但您不应该直接从您的课程 PersonConstructService 调用 repositories 方法。没问题,如果你的班级需要 7 个服务,为什么调用这 7 个服务不好呢?而且我猜如果您的 POJO 与 (@ManyToOne, @OneToMany, ...) 之类的关系绑定在一起,您可以简单地构造您的对象而无需手动添加其他对象。
  • @Alex 好吧,如果您的类构造函数中有这么多服务,据我所知,根据 Spring Docs 的代码味道:As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns. 您是否建议通过以下方式进行字段注入:With Spring you can at first use @Autowired annotation to get your services ready-for-use in your class
  • 完全可以。构造函数参数是一回事,但注入你的服务是另一种正确地做你想做的事情的方法。 Java EE 已经在 v6 中使用 @Inject 实现了这一点,Spring 官方关键字是 @Autowired,我个人在我的一些类中有时会有 6 或 7 个。我从未见过特定问题
  • @Alex 它会起作用,但你不能确定你的服务不为空。通过构造函数注入,这些东西在编译时被检查。虽然您无法确定字段。
  • Appart 从构造函数与 setter 论点出发,我确实认为您的示例代码设计得很好。您有将数据库表映射到对象概念的存储库。您在其之上使用服务层来构造更多抽象的域对象。这对我来说看起来不错你的“人”抽象需要构建许多存储库这一事实是另一个问题(你的数据库模型化是否足够?)。但考虑到它是什么,在我看来,在服务层聚合 DAO/存储库以构建高级业务模型抽象实际上是一种很好的做法。

标签: java spring spring-data spring-data-jpa


【解决方案1】:

我不认为您可以/应该创建像抽象这样的元存储库。存储库具有明确定义的含义,从概念上讲,它们是您的 Hibernate/JPA/Datastore 实体的 CRUD 服务(有时更多:-))。我想这对他们来说已经足够了。再多的就让人迷惑了。

现在我要建议的是一种构建“Person”对象的“智能”方式,它可以自动识别任何有助于 Person 对象含义的新服务。

关键在于:

  • 您可以让您的存储库实现一个给定的接口,比如PersonDataProvider,它会有一个方法,比如public PersonPart contributeDataToPersonBuidler(PersonBuilder)
  • 您可以让您的 @Service 实现 Spring 的 BeanFactoryPostProcessor 接口,允许您检查容器中的所有此类 PersonDataProvider 实例,并将它们注入您的服务(请参阅How to collect and inject all beans of a given type in Spring XML configuration 接受的答案)
  • 您的@Service 实现将依次要求所有PersonDataProviders 要求他们提供数据。

我可以扩大一点,但在我看来,这是要走的路。

有人可能会争辩说这是不干净的(它使您的存储库知道发生在服务层的“某些事情”,他们不应该这样做),并且可以解决这个问题,但公开要点更简单的解决方案。

编辑:自从第一次写这篇文章以来,我意识到 Spring 可以自动检测和注入某种类型的所有 bean,而不需要 PostProcessors。在此处查看已接受的答案:Autowire reference beans into list by type

【讨论】:

  • Autowire 是一个不错的方法。但是,如何根据特定条件从 repos 列表中获取所需的 repo?
  • 问题是,我记得“我怎样才能得到所有这些”,而不是“选择其中一个”。如果你想选择,你有多种选择。如果您在构建时知道如何选择,请为您的 bean 命名(@Named/@Qualifier 等)。如果只能在启动时知道,看能不能用Spring EL,如果只能在运行时知道,那就得建一个方法(比如public boolean supports(YourObject object))来选择调用哪个。跨度>
  • 好的,就我而言,我必须在运行时解决它,所以我想我会采用第二种方法。谢谢
【解决方案2】:

我认为它是一个非常合理和实用的服务层数据聚合。 在春天是完全可以实现的。如果您有权访问存储库代码,则可以将它们全部命名为:

@Repository("repoOne")
public class RepositoryOne {

@Repository("repoTwo")
public class RepositoryTwo {

并根据需要将它们注入聚合服务:

@Service
public class MultipleRepoService {

    @Autowired
    @Qualifier("repoOne")
    private RepositoryOne repositoryOne;

    @Autowired
    @Qualifier("repoTwo")
    private RepositoryTwo repositoryTwo;

    public void doMultipleBusiness() {
        repositoryOne.one();
        repositoryTwo.two();
    }
}

实际上,如果它们是不同的类,你甚至不需要命名和限定它们,但如果它们在层次结构中或具有相同的接口......

另外,如果自动装配不是这种情况,您可以直接注入构造方法:

public void construct(@Qualifier("repoOne")RepositoryOne repoOne,
                          @Qualifier("repoTwo")RepositoryTwo repoTwo) {
        repoOne.one();
        repoTwo.two();
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-08
    • 2015-04-07
    • 1970-01-01
    • 1970-01-01
    • 2019-07-05
    • 2015-02-13
    • 2019-07-30
    • 1970-01-01
    相关资源
    最近更新 更多