Hibernate HSQL 使用嵌套列表选择新的

Posted

技术标签:

【中文标题】Hibernate HSQL 使用嵌套列表选择新的【英文标题】:Hibernate HSQL select new with nested List 【发布时间】:2020-07-14 01:46:01 【问题描述】:

我已经搜索过这个问题,并且在过去的几天里一直被卡住,所以我希望你能帮忙。我将尝试用一个基本概念来总结我遇到的问题,因为这样我就可以将其应用于我更复杂的问题。

我有 2 个实体对象:学生和课程。请注意,学生实体创建了一个包含 3 列的表,并且还有一个额外的“金额”属性。这里的想法是我不想存储我在执行查询时将执行的聚合。所以学生购买了几门课程,每门课程都有成本。我想总结总成本,使其在 json 中处于同一级别(请参阅期望的结果)。

我卡住的地方是我的选择查询。当我删除 a.course 时一切正常。我的 json 显示了聚合,一切都很好。但显然,当然缺少清单。所以我的总体问题是,当我的查询中嵌入了嵌套列表对象时,我的选择查询应该是什么样子?

select new Student(a.id, a.name, a.course,

期望的结果:


"id": 1,
"name": "Billy"
"course":
     [
         "id" : 1,
         "courseName" : "Math",
         "cost" : 12.99
       ],[
         "id" : 2,
         "courseName" : "Science",
         "cost" : 15.99
       ]
     
"amount" : 28.98


public class Student 
@Id
@GeneratedValue(
        strategy= GenerationType.AUTO,
        generator="PRIVATE_SEQ"
    )
    @GenericGenerator(
        name = "native",
        strategy = "native"
    )
private long id;

@Column(name = "name", nullable = false, length = 25)
private String name;

@OneToMany(fetch=FetchType.EAGER)
@JoinColumn(name="student_id")
private List<Course> course;

private BigDecimal amount;

public Student(Long id, String name,  List<Course> course, BigDecimal amount) 
    this.id = id;
    this.name = name;
    this.course = course;
    this.amount = amount;

@Table(name = "course")
public class Course 
@Id
@GeneratedValue(
        strategy= GenerationType.AUTO,
        generator="PRIVATE_SEQ"
    )
    @GenericGenerator(
        name = "native",
        strategy = "native"
    )
private long id;

@Column(name = "coursename", nullable = false, length = 30)
private String courseName;


@ManyToOne
@JoinColumn(name="student_id")
private Student student;

@Column(name = "cost", nullable = false)
private BigDecimal cost;

public Course(Long id, String courseName, BigDecimal cost) 
    this.id = id;
    this.courseName = courseName;
    this.cost = cost;


List<Student> students = session
        .createQuery(" select new Student(a.id, a.name, a.course, SUM(CASE WHEN b.cost <> 0 THEN b.cost ELSE 0 END)) from Student a "
                + "Left join fetch Course b on b.id = d.budget "
                + "group by b.id ")
        .getResultList();

【问题讨论】:

【参考方案1】:

您可以使用子查询来解决这个问题,但根本不可能将 JPQL 构造函数表达式与集合一起使用。它仅适用于标量。

这是Blaze-Persistence Entity Views 的完美用例。

Blaze-Persistence 是基于 JPA 的查询构建器,它支持 JPA 模型之上的许多高级 DBMS 功能。我在它之上创建了实体视图,以允许在 JPA 模型和自定义接口定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您以您喜欢的方式定义您的目标结构,并通过 JPQL 表达式将属性(getter)映射到实体模型。由于属性名称用作默认映射,因此您通常不需要显式映射,因为 80% 的用例是拥有作为实体模型子集的 DTO。

模型的 DTO 映射可能如下所示简单

@EntityView(Student.class)
interface StudentDto 
    long getId();
    String getName();
    List<CourseDto> getCourses();
    @Mapping("SUM(courses.cost) OVER (PARTITION BY id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)")
    BigDecimal getAmount();

@EntityView(Course.class)
interface CourseDto 
    long getId();
    String getCourseName();
    BigDecimal getCost();

我在这里使用窗口函数,不确定 HSQLDB 是否支持,但您也可以使用子查询或简单地为集合选择不同的获取策略以使聚合正常工作。

@EntityView(Student.class)
interface StudentDto 
    long getId();
    String getName();
    @Mapping(fetch = SUBSELECT)
    List<CourseDto> getCourses();
    @Mapping("SUM(courses.cost)")
    BigDecimal getAmount();

我不明白的是,当您要获取所有课程时,为什么要在 SQL 中计算金额。您可以像这样简单地计算 Java 中的金额:

@EntityView(Student.class)
interface StudentDto 
    long getId();
    String getName();
    List<CourseDto> getCourses();
    default BigDecimal getAmount() 
        return getCourses().stream().map(CourseDto::getCost).reduce(BigDecimal.ZERO, BigDecimal::add);
    

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

StudentDto dto = entityViewManager.find(entityManager, StudentDto.class, id);

但是 Spring Data 集成允许您几乎像 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

它只会获取您告诉它获取的映射

【讨论】:

以上是关于Hibernate HSQL 使用嵌套列表选择新的的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Hibernate 将模式名称添加到 Hsql 函数?

用于 HSQL 的宠物诊所 hibernate.dialect

Hibernate @Where 子句在 HSQL 和 MySQL 之间不一致

org.hibernate.HibernateException:没有使用 HSQL DB 配置 CurrentSessionContext

在 LightAdmin (Spring/JPA/Hibernate) 中使 HSQL 数据库持久化

使用hsql hibernate数据源隔离Junit测试