【问题标题】:Spring Data JPA Specification Paging with Two Tables带有两个表的 Spring Data JPA 规范分页
【发布时间】:2021-11-09 16:08:20
【问题描述】:

我有两个表:收入和支出,其中存储了我所有的收入和支出交易,目前这两个表之间没有关系。

@Entity
@Table(name = "expense")
public class Expense {

    private BigDecimal amount;
    private LocalDate dueDate;
    private LocalDate datePaid;
    private String description;
    ......
}

@Entity
@Table(name = "income")
public class Income {

    private BigDecimal amount;
    private LocalDate dueDate;
    private LocalDate datePaid;
    private String description;
    ......
}

目前我使用 Spring Data JPA 的规范功能在单个实体上执行此操作:

费用: Page<Expense> expensePage = expenseRepository.findAll(expenseSpecification, paginatedList);

对于收入: Page<Income> depositPage = incomeRepository.findAll(incomeSpecification, paginatedList);

现在,我想在一张表上显示我的所有交易(收入和支出)。因此,我现在正在探索性能有效的解决方案,以便在应用过滤器和分页后从这两个表中获取数据。

我想使用规格,因为过滤器(金额、日期等...)是可选的。

我可以想到以下几种方法:

  1. 如上所述单独运行查询,合并结果,然后手动应用分页 - 从性能角度来看,这似乎非常无效,因为它将获取整个表数据
  2. 将本机 SQL 查询与 UNION 结合使用,但无法应用过滤器,因为过滤器是动态且可选的

选择 * 来自 ( 选择 ID, 数量, paid_on 为 "date_paid", “费用”作为“类型” 从 公共费用 联盟 选择 ID, 数量, received_on 作为“date_paid”, '订金' 从 公共存款 ) 作为 annon_1 在哪里 annon_1.amount > 2 和 annon_1.date_paid = '2021-08-10' 订购 annon_1.id 限制 5 偏移量 0

  1. 实现继承,因为收入和费用基本上都是事务,具有一些共享属性,如日期、金额、描述等……但这将是一个很大的变化,因为这两个表中已经有很多数据。所以我必须更改数据库架构、域类和迁移来管理现有数据

或者是否有其他性能有效的解决方案?

【问题讨论】:

  • 如果您只是读取数据,您还可以创建映射到相同表并使用继承的“只读”实体。
  • @Thomas 是的,我只是在读取数据。你是说@MappedSuperclass继承策略?
  • MappedSuperclass 实际上并不是一种继承策略,至少不是我定义它的方式。它提供共享属性,但实体不相关,您无法进行组合查询。假设您需要 2 个不同的表 InheritanceType.TABLE_PER_CLASS。
  • @Thomas InheritanceType.TABLE_PER_CLASS 的问题是我们不能再使用身份密钥生成。
  • @Thomas 这将需要大量的架构更改,因为父实体也将映射到某个表。它实际上是我在问题中提到的第三个策略,对吧?

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


【解决方案1】:

您还有另一种选择,即使用@Subselect 实体。

@Entity
@Subselect("select i.id as id, true as income, i.description as description, i.date_paid as date_paid, i.due_date as due_date, i.amount as amount from income i union all select e.id as id, false as income, e.description as description, e.date_paid as date_paid, e.due_date as due_date, e.amount as amount from expense e")
public class Transaction {
    @EmbeddedId
    private TransactionId id;
    @Column(name = "amount")
    private BigDecimal amount;
    @Column(name = "due_date")
    private LocalDate dueDate;
    @Column(name = "date_paid")
    private LocalDate datePaid;
    @Column(name = "description")
    private String description;
}

@Embeddable
public class TransactionId {
    @Column(name = "id", nullable = false)
    private Long id;
    @Column(name = "income", nullable = false)
    private boolean income;
}

然后您可以像查询收入和支出一样查询它。

最重要的是,我可以建议您为此目的使用 Blaze-Persistence,它在 JPA/Hibernate 之上工作,并添加对 UNION ALL 等集合操作的支持,以防您需要更动态地创建此 UNION,即更改基于一些运行时条件的查询。我也认为这是Blaze-Persistence Entity Views 的完美用例。

我创建了该库以允许在 JPA 模型和自定义接口或抽象类定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您按照自己喜欢的方式定义目标结构(域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。

使用 Blaze-Persistence Entity-Views 的用例的 DTO 模型可能如下所示:

@EntityView(Transaction.class)
public interface TransactionDto {
    @IdMapping
    TransactionIdDto getId();
    String getDescription();
    LocalDate getDatePaid();
    LocalDate getDueDate();
    BigDecimal getAmount();

    @EntityView(TransactionId.class)
    interface TransactionIdDto {
        @IdMapping
        Long getId();
        boolean isIncome();
    }
}

查询是将实体视图应用于查询的问题,最简单的就是通过 id 进行查询。

TransactionDto a = entityViewManager.find(entityManager, TransactionDto.class, id);

Spring Data 集成让您可以像使用 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<TransactionDto> findAll(Pageable pageable);

最好的部分是,它只会获取实际需要的状态!

【讨论】:

    猜你喜欢
    • 2020-05-20
    • 1970-01-01
    • 2016-07-22
    • 1970-01-01
    • 2016-02-17
    • 2017-01-12
    • 2019-05-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多