Spring Data JPA Specification поиск свойства во вложенной коллекции - PullRequest
0 голосов
/ 03 августа 2020

Допустим, у меня есть как минимум две сущности.

@Entity
public class Process {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true)
    private String name;

    @ManyToAny(
            metaColumn = @Column(name = "node_type"),
            fetch = FetchType.LAZY
    )
    @AnyMetaDef(
            idType = "long", metaType = "string",
            metaValues = {
                    @MetaValue(targetEntity = Milestone.class, value = MILESTONE_DISC),
                    @MetaValue(targetEntity = Phase.class, value = PHASE_DISC)
            }
    )
    @Cascade({org.hibernate.annotations.CascadeType.ALL})
    @JoinTable(
            name = "process_nodes",
            joinColumns = @JoinColumn(name = "process_id", nullable = false),
            inverseJoinColumns = @JoinColumn(name = "node_id", nullable = false)
    )
    private Collection<ProcessNode> nodes = new ArrayList<>();

    ...
}
@Entity
@ToString
@DiscriminatorValue(MILESTONE_DISC)
public class Milestone implements ProcessNode {


    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private Collection<ResultDefinition> results;

    @ToString.Exclude
    @ManyToOne(fetch = FetchType.LAZY)
    @Transient
    private Process process;

...
}

Теперь я хочу использовать спецификацию jpa данных Spring, чтобы найти (все) процессы, у которых есть веха с именем «S5».

Обратите внимание, что Milestone является ProcessNode, а есть еще одна сущность с именем Phase который также является ProcessNode. Они могут содержаться в коллекции «узлов» моей сущности процесса.

структура базы данных

Пытался написать примерно так:

    public static Specification<Process> hasMilestoneWithName(final String milestoneName) {
    return (Specification<Process>) (root, query, criteriaBuilder) -> {
        Path<?> namePath = root.join("nodes").get("name");
        return criteriaBuilder.equal(namePath, milestoneName);
    };
}

Не работает, а выкидывает:

Вызвано: java. lang.IllegalArgumentException: невозможно найти атрибут с заданным именем [узлы] в этом ManagedType [com.smatrics.dffs.processservice.model.entities.Process]

Я действительно не знаю, как использовать API. Примеры часто относятся к метамодели, которая будет сгенерирована IDE или maven, но я действительно не хочу, чтобы какие-либо сгенерированные stati c ресурсы. Пожалуйста, помогите мне решить эту проблему с помощью спецификации spring-data-jpa без сгенерированной мета-модели.

Также, если бы вы могли помочь мне написать hql, это было бы здорово.

Спасибо!

1 Ответ

1 голос
/ 03 августа 2020

Я бы предложил более простую альтернативу, идущую снизу вверх:

  • Загрузить Milestone сущности с name=S5: findByName("S5")
  • Вернуть Process для каждый Milestone
  • Отфильтровать дубликаты

Или вы даже можете сохранить несколько запросов SQL, вернув не сущность Milestone, а только идентификатор Process для каждого Milestone, а затем загрузить узлы Process по списку идентификаторов:

(собственный) SQL эквивалент будет

select *
from process
where id in (
  select process_id
  from milestone
  where name = 'S5'
)

Независимо от моего решения ваш join мне кажется не совсем правильным, но я не могу указать, что не так - может быть, есть другие методы в метамодели JPA, которые возвращают CollectionJoin? Точно сказать не могу. Вероятно, это связано с тем, что @ManyToAny не является стандартом JPA, поэтому API критериев JPA не распознает nodes как допустимое «присоединяемое» поле.

...