【问题标题】:Multiple Repositories for the Same Entity in Spring Data RestSpring Data Rest 中同一实体的多个存储库
【发布时间】:2016-07-06 20:45:08
【问题描述】:

是否可以使用 Spring Data Rest 为同一个 JPA 实体发布两个不同的存储库? 我为这两个存储库提供了不同的路径和 rel 名称,但两者中只有一个可用作 REST 端点。 我有两个存储库的原因是,其中一个是摘录,仅显示实体的基本字段。

【问题讨论】:

  • 这是不可能的。使用 Spring Data REST,托管资源是一个实体而不是存储库。该库维护一个Map 的托管资源,其中键是实体类。因此,实体一次只能映射到一个存储库接口(因为Map 只能为键保存一个值)。如果您的应用程序对每个实体类拥有多个存储库至关重要,您可能需要向 Spring Data 团队提出增强请求。
  • 有MultiMaps。从技术上讲,肯定没有障碍。从语义的角度来看,在我们的用例中,资源不能用实体来标识,而是用数据库来标识。视图对应于 Spring Data Rest 中的 Projections。因此,如果我可以将投影和操作映射到资源,那就太好了。
  • 我的评论是基于actual Spring Data REST implementation。我知道有像MultiMaps 这样的结构,这就是为什么我建议您考虑向 Spring Data 团队提出增强请求。
  • 对于您的评论,表格及其视图对我来说是独立的对象。如果您必须执行 DDL 才能触发 DML 查询,那将成为一个单独的对象。如果数据库将表和视图分开处理,那么其他应用程序层也应如此。考虑到这一点,一个表将映射到它自己的 JPA 实体,而一个视图将映射到它自己单独的实体。然后两者都可以拥有自己的存储库接口,并且现有的 Spring Data REST 基础架构可以正常工作。

标签: spring spring-data spring-data-jpa spring-data-rest


【解决方案1】:

我最终使用 @Subselect 创建了第二个不可变实体并将其绑定到第二个 JpaRepsotory 并将其设置为 @RestResource(exported = false),这也鼓励了关注点分离。

员工示例

@Entity
@Table(name = "employee")
public class Employee {

    @Id
    Long id

    String name

    ...

}
@RestResource
public interface EmployeeRepository extends PagingAndSortingRepository<Employee, Long> {

}
@Entity
@Immutable   
@Subselect(value = 'select id, name, salary from employee')
public class VEmployeeSummary {

    @Id
    Long id

    ...

}
@RestResource(exported = false)
public interface VEmployeeRepository extends JpaRepository<VEmployeeSummary, Long> {

}

上下文

单体应用程序中的两个包有不同的要求。需要在 PagingAndSortingRepository 中公开 UI 的实体,包括 CRUD 函数。另一个是聚合后端报表组件,没有分页但有排序。

我知道我可以在请求 Pageable.unpaged() 后从 PagingAndSorting 存储库中过滤结果,但我只想要一个基本 JPA 存储库,它为某些过滤器返回 List

【讨论】:

    【解决方案2】:

    因此,这并不能直接回答问题,但可能有助于解决根本问题。

    每个实体只能有一个存储库...但是,每个表可以有多个实体;因此,每个表有多个存储库。

    在我编写的一些代码中,我必须创建两个实体...一个具有自动生成的 ID,另一个具有预设 ID,但都指向同一个表:

    @Entity
    @Table("line_item")
    public class LineItemWithAutoId {
    
        @Id
        @GeneratedValue(generator = "system-uuid")
        @GenericGenerator(name = "system-uuid", strategy = "uuid")
        private String id;
    
        ...
    }
    
    
    
    @Entity
    @Table("line_item")
    public class LineItemWithPredefinedId {
    
        @Id
        private String id;
    
        ...
    }
    

    然后,我为每个都有一个存储库:

    public interface LineItemWithoutId extends Repository<LineItemWithAutoId,String> {
    
        ...
    
    }
    
    
    public interface LineItemWithId extends Repository<LineItemWithPredefinedId,String> {
    
        ...
    
    }
    

    对于发布的问题,您可以有两个实体。一个是完整的实体,所有东西都有 getter 和 setter。另一个是实体,其中有所有内容的设置器,但只有您想要公开的字段的获取器。这有意义吗?

    【讨论】:

    • 我试过了,但 Hibernate 似乎对此并不满意:org.hibernate.tool.schema.spi.SchemaManagementException: SQL 字符串为 line_item 添加了不止一次
    【解决方案3】:

    可怕的部分不仅在于每个实体只能有 1 个 spring 数据存储库 (@RepositoryRestResource),而且还有如果你有一个常规的 JPA @Repository(如 CrudRepository 或 PagingAndSorting)它还将与弹簧数据休息一交互(因为地图中的关键是实体本身)。 浪费了好几个小时调试其中一个或另一个的随机负载。我猜如果这是 spring 数据的硬性限制,那么在尝试覆盖值时,如果地图的键已经存在,则至少会引发异常。

    【讨论】:

    • 我为此创建了一个 Jira 问题:jira.spring.io/browse/DATAREST-923 随意投票!
    • 示例用例:存储在 JPA 中 + 使用 Elasticsearch 存储库进行搜索
    • @Tim:我们有类似的要求,需要两个不同的存储库(一个指向 MySQL 主实例,另一个指向具有只读访问权限的从属实例)。所有基于用户的交易都应该与我们的主仓库交互。批处理框架(仅执行读取)应与从属实例交互。
    【解决方案4】:

    答案似乎是:每个实体只能有一个存储库。

    【讨论】:

      猜你喜欢
      • 2017-12-02
      • 2014-06-21
      • 2018-05-25
      • 2016-01-07
      • 1970-01-01
      • 2019-02-14
      • 2020-04-12
      • 1970-01-01
      • 2019-09-30
      相关资源
      最近更新 更多