Hibernate H SQL выберите новый с вложенным списком - PullRequest
0 голосов
/ 14 июля 2020

Я искал эту проблему и застрял в течение последних нескольких дней, поэтому я надеюсь, что вы можете помочь. Я попытаюсь обобщить проблему, с которой я столкнулся, с помощью базовой c концепции, потому что тогда я могу просто применить ее к моей более сложной проблеме.

У меня есть 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 Ответ

0 голосов
/ 16 июля 2020

Вы можете решить эту проблему с помощью подзапроса, но просто невозможно использовать выражение конструктора JPQL с коллекциями. Он работает только со скалярами.

Это идеальный вариант использования для представлений сущностей Blaze-Persistence .

Blaze-Persistence - это построитель запросов поверх JPA, который поддерживает многие расширенные функции СУБД на вершине модели JPA. Я создал Entity Views поверх него, чтобы упростить сопоставление между моделями JPA и моделями, определяемыми пользовательским интерфейсом, что-то вроде Spring Data Projection на стероидах. Идея состоит в том, что вы определяете свою целевую структуру так, как вам нравится, и сопоставляете атрибуты (геттеры) через выражения JPQL с моделью сущности. Поскольку имя атрибута используется в качестве сопоставления по умолчанию, вам в большинстве случаев не нужны явные сопоставления, поскольку в 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);
    }
}

Запрос - это вопрос применения представления сущности к запросу, самым простым из которых является запрос по идентификатору.

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

Но интеграция Spring Data позволяет вам использовать его почти как Spring Data Projection: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring -data-features

Он будет только получать сопоставления что вы говорите ему получить

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...