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。我知道有像MultiMap
s 这样的结构,这就是为什么我建议您考虑向 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 json Response 中动态过滤实体字段
如何在 REST 端点中发布具有关系的实体 - Kotlin Spring Data