Spring-Data-JPA с QueryDslPredicateExecutor и присоединением к коллекции

предположим, у меня есть такая модель данных (псевдокод):

@Entity
Person {
    @OneToMany
    List<PersonAttribute> attributes;
}

@Entity
PersonAttribute {
    @ManyToOne
    AttributeName attributeName;

    String attributeValue;
}

@Entity
AttributeName {
    String name;
}

у меня есть репозиторий Spring-Data-JPA, определенный как:

public interface PersonRepository extends PagingAndSortingRepository<Person, Long>, QueryDslPredicateExecutor<Person>{}

Я вижу в документации QueryDSL, что есть механизм для присоединения от человека к PersonAttribute, но похоже, что вам нужен доступ к объекту запроса QueryDsl, которого у клиента репозитория не было бы.

что я хотел бы сделать с моим сказуемым, чтобы найти всех тех людей, которые имеют AttributeValue (есть одно соединение) со значением "синий" и AttributeName (есть другое соединение) с именем "eyecolor". Я не уверен, как бы я сделал это с any() и убедитесь, что я получаю только те, у кого eye_color=blue, а не те, у кого shoe_color=blue.

Я надеялся, что смогу сделать что-то вроде этого:

QPerson person = QPerson.person;
QPersonAttribute attribute = person.attributes.any();

Predicate predicate = person.name.toLowerCase().startsWith("jo")
    .and(attribute.attributeName().name.toLowerCase().eq("eye color")
          .and(attribute.attributeValue.toLowerCase().eq("blue")));

но с any() там он просто соответствует чему-либо со значением атрибута "синий" и что-либо с атрибутом "цвет глаз" независимо от цвета. как я могу применить эти условия к одному и тому же атрибуту в наборе?

1 ответов


вы не можете напрямую присоединиться к столбцу в предикате, но вы можете создать выражения any (), подобные этому

QPerson.person.attributes.any().attributeValue.eq("X")

этот подход имеет ограничение, что выражение соединения QPerson.person.attributes.any() может использоваться только в одном фильтре. Это имеет, хотя преимущество, что это выражение внутренне преобразуется в подзапрос, который не конфликтует с подкачкой.

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

QPersonAttribute attribute = QPersonAttribute.personAttribute;
new JPASubQuery().from(attribute)
    .where(attribute.in(person.attributes),
           attribute.attributeName().name.toLowerCase().eq("eye color"),
           attribute.attributeValue.toLowerCase().eq("blue"))
     .exists()

кроме QueryDslPredicateExecutor вы также можете использовать запросы Querydsl через данные Spring, как это

public class CustomerRepositoryImpl
 extends QueryDslRepositorySupport
 implements CustomerRepositoryCustom {

    public Iterable<Customer> findAllLongtermCustomersWithBirthday() {
        QCustomer customer = QCustomer.customer;
        return from(customer)
           .where(hasBirthday().and(isLongTermCustomer()))
           .list(customer);
    }
}

пример взят отсюда https://blog.42.nl/articles/spring-data-jpa-with-querydsl-repositories-made-easy/