Использование QueryDSL для запроса к базе данных точек с близким расстоянием

У меня есть следующие объекты в моем приложении:

  • Member
  • FamilyAdvertisment
  • Address

В объекте Member:

@OneToOne(cascade=CascadeType.ALL)
    private Address address;
...
@OneToMany(fetch = FetchType.LAZY, mappedBy = "member")
private List<Advertisement> advertisements;

В объекте Advertisement:

@NotNull
@ManyToOne(fetch = FetchType.LAZY)
private Member member;

Полный Address объект:

@Entity
public class Address {

    private String formattedAddress;
    private double latitude;
    private double longitude;
}

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

Вот что я придумал:

QFamilyAdvertisement qFamilyAdvertisement = QFamilyAdvertisement.familyAdvertisement;

NumberPath<Double> lat = qFamilyAdvertisement.member.address.latitude;//NPE
NumberPath<Double> lng = qFamilyAdvertisement.member.address.longitude;
NumberPath<Double> distance = null;
NumberExpression<Double> formula = 
        (acos(cos(radians(Expressions.constant(requiredAddress.getLatitude())))
        .multiply(cos(radians(lat))
        .multiply(cos(radians(lng).subtract(radians(Expressions.constant(requiredAddress.getLongitude())))
        .add(sin(radians(Expressions.constant(requiredAddress.getLatitude())))
        .multiply(sin(radians(lat))))))))
        .multiply(Expressions.constant(6371)));

List<FamilyAdvertisement> foundFamilyAdvertisements = from(qFamilyAdvertisement.member.address).where(formula.as(distance).lt(20)).list(qFamilyAdvertisement);

Однако кажется, что я неправильно использую класс NumberPath, так как продолжаю получать NPE. Может ли кто-нибудь помочь мне правильно оформить мой запрос?

изменить: я изменил свой объект FamilyAdvertisement следующим образом:

@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@QueryInit("address")
private Member member;

Теперь я получаю следующее исключение:

java.lang.IllegalArgumentException: Only root paths are allowed for joins : familyAdvertisement.member.address
    com.mysema.query.DefaultQueryMetadata.ensureRoot(DefaultQueryMetadata.java:208)
    com.mysema.query.DefaultQueryMetadata.validateJoin(DefaultQueryMetadata.java:132)
    com.mysema.query.DefaultQueryMetadata.addJoin(DefaultQueryMetadata.java:118)
    com.mysema.query.DefaultQueryMetadata.addJoin(DefaultQueryMetadata.java:110)
    com.mysema.query.support.QueryMixin.from(QueryMixin.java:161)
    com.mysema.query.jpa.JPQLQueryBase.from(JPQLQueryBase.java:96)
    com.mysema.query.jpa.impl.JPAQuery.from(JPAQuery.java:30)
    org.springframework.data.jpa.repository.support.Querydsl.createQuery(Querydsl.java:88)
    org.springframework.data.jpa.repository.support.QueryDslRepositorySupport.from(QueryDslRepositorySupport.java:94)
    com.bignibou.repository.FamilyAdvertisementRepositoryImpl.performFamilyAdvertisementSearch(FamilyAdvertisementRepositoryImpl.java:64)

Строка 64 это:

List<FamilyAdvertisement> foundFamilyAdvertisements = from(qFamilyAdvertisement.member.address).where(formula.as(distance).lt(20)).list(qFamilyAdvertisement);

Любая подсказка, что не так сейчас?

edit2: я забыл упомянуть, что FamilyAdvertisement расширяет Advertisement и что переменная member находится в Advertisement.

edit3: вот SQL, который я пытаюсь воспроизвести с помощью QueryDSL:

select * from family_advertisement a inner join member m
on a.member = m.id
where m.address
in (
SELECT id 
FROM address where 
 6371 * 
acos( cos( radians(48.8558966) ) 
* cos( radians( latitude ) ) 
* cos( radians( longitude ) - radians(2.3622728) ) 
+ sin( radians(48.8558966) )
* sin( radians( latitude ) )
) < 20);

Я пробовал что-то вроде этого:

List<FamilyAdvertisement> foundFamilyAdvertisements = from(qFamilyAdvertisement).where(qFamilyAdvertisement.member.address.in(

                new JPASubQuery().from(QAddress.address).where(formula.lt(20)))

                ).list(qFamilyAdvertisement);

где формула приведена выше, но я не уверен, как выразить некоррелированный подзапрос в QueryDSL, и особенно оператор in выше кажется проблематичным...

изменить4:

Теперь работает следующий подзапрос:

List<FamilyAdvertisement> foundFamilyAdvertisements = 
        from(qFamilyAdvertisement).where(qFamilyAdvertisement.member.address.in(new JPASubQuery().from(QAddress.address).where(formula.lt(20)).list(QAddress.address))).list(qFamilyAdvertisement);

person balteo    schedule 07.04.2013    source источник
comment
Я опробовал ваш метод и даже создал адреса с тем же gps. Тем не менее, в результате ничего не было возвращено.   -  person Ifesinachi Bryan    schedule 22.04.2020


Ответы (1)


Этот путь слишком длинный для нетерпеливой инициализации

qFamilyAdvertisement.member.address.latitude;

Подробнее об инициализации пути в Querydsl читайте здесь http://www.querydsl.com/static/querydsl/3.1.0/reference/html/ch03s04.html#d0e1699

person Timo Westkämper    schedule 07.04.2013
comment
Привет Тимо. Я изменил свою сущность и теперь получаю: IllegalArgumentException: Only root paths are allowed for joins : familyAdvertisement.member.address. Я отредактировал свой пост. - person balteo; 07.04.2013
comment
Аргумент from должен быть корневым путем, переменной. - person Timo Westkämper; 07.04.2013
comment
Я пытаюсь использовать подзапрос, но метод in не принимает подзапрос, как в sql. Любая идея, как сделать это правильно, Тимо? - person balteo; 08.04.2013