Spring Data Rest 中同一实体的多个存储库

Posted

技术标签:

【中文标题】Spring Data Rest 中同一实体的多个存储库【英文标题】:Multiple Repositories for the Same Entity in Spring 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 基础架构可以正常工作。 【参考方案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】:

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

【讨论】:

以上是关于Spring Data Rest 中同一实体的多个存储库的主要内容,如果未能解决你的问题,请参考以下文章

Spring Data Rest - 按多个属性排序

Spring Data Rest 中嵌入实体的链接

在 Spring Data rest json Response 中动态过滤实体字段

如何在 REST 端点中发布具有关系的实体 - Kotlin Spring Data

如何简单地添加指向 Spring Data REST 实体的链接

在 Spring Boot Data REST 中进行实体验证后运行 @HandleBeforeCreate