У меня есть следующие объекты в моем приложении:
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);