如何在 JPAQuery 中使用 INNER JOIN?
How to use INNER JOIN in JPAQuery?
我确切地说,我是 Java 开发人员一年级的法国学生。
而且我很确定我的问题标题对于我正在尝试做的事情甚至都不正确。那我希望下面的解释会更清楚!
我正在开发一个 multi-module 小应用程序,使用:Spring 启动、Spring 安全、Hibernate、Spring 数据、Spring MVC 和 Thymeleaf .
这里是my repo
我目前正在开发一种方法来处理 multi-criteria 请求。
我正在使用 Querydsl。
这里是涉及的部分域:
母实体
@Entity
@Table(name="element")
@Inheritance(strategy = InheritanceType.JOINED)
@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public abstract class Element {
//----------ATTRIBUTES----------
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@NotNull
@Size(min=4,max=50)
private String name;
private LocalDateTime createDate;
private LocalDateTime updateDate;
@OneToMany/*(mappedBy = "element")*/
@JoinColumn(name = "element_id")
private List< Comment > comments;
//..
child 个实体
/**
* Bean used to define a book or a document that contains one or many climbing areas
*/
@Entity
@Table(name="atlas")
@PrimaryKeyJoinColumn(name = "element_id")
@Getter
@Setter
@NoArgsConstructor
@ToString( exclude = {"areas" , "bookingRequests" , "user"})
public class Atlas extends Element {
//----------ATTRIBUTES----------
@ManyToMany
@JoinTable(
name = "areas_in_atlases",
joinColumns = { @JoinColumn(name = "atlas_id") },
inverseJoinColumns = { @JoinColumn(name = "area_id") } )
private List< Area > areas;
private String scale;
private String country;
private String region;
private String department;
private boolean available;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "atlas", cascade = CascadeType.ALL)
private List<BookingRequest> bookingRequests;
//..
/**
* Bean used to define an area with one or many climbing crag
*/
@Entity
@Table(name="area")
@PrimaryKeyJoinColumn(name = "element_id")
@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode( callSuper = true )
public class Area extends Element {
//----------ATTRIBUTES----------
@ManyToMany(mappedBy = "areas")
private List< Atlas > atlases;
private Atlas atlas;
@OneToMany/*(mappedBy = "area", fetch = FetchType.LAZY)*/
@JoinColumn(name = "area_id")
private List< Crag > crags;
private int approachDuration;
private String locality;
@OneToMany/*(mappedBy = "area", fetch = FetchType.LAZY)*/
@JoinColumn(name = "area_id")
private List< Parking > parking;
private String rockType;
//..
我的 multi-criteria 方法的第一部分正在运行(我猜不是很优雅......):
@Override
public Page<Atlas> searchAtlasByNameAndCountryAndRegionAndDepartment( int page, int size, String name, String country, String region, String department ){
QAtlas qAtlas = QAtlas.atlas;
ArrayList< Predicate > predicates = new ArrayList<>();
List<Atlas> atlases;
if( !name.equals( "" ) ){
predicates.add( qAtlas.name.containsIgnoreCase(name) );
}
if( !country.equals( "" ) ){
predicates.add( qAtlas.country.containsIgnoreCase(country) );
}
if( !region.equals( "" ) ){
predicates.add( qAtlas.region.containsIgnoreCase(region) );
}
if( !department.equals( "" ) ){
predicates.add( qAtlas.department.containsIgnoreCase(department) );
}
BooleanBuilder booleanBuilder = new BooleanBuilder();
for ( Predicate predicate: predicates ) {
booleanBuilder.and(predicate);
}
atlases = ( List< Atlas > ) getDaoFactory().getAtlasRepository().findAll( booleanBuilder );
long total = ( long ) atlases.size( );
return new PageImpl(atlases, PageRequest.of( page, size ), total );
}
但我坚持使用在 Area 中搜索的方法,使用仅在 Atlas 中的字段,例如 Country。
@Override
public Page< Area > searchAreaByNameAndCountryAndRegionAndDepartment( int page, int size, String name, String country, String region, String department ){
QAtlas qAtlas = QAtlas.atlas;
QArea qArea = QArea.area;
final JPAQueryFactory queryFactory = new JPAQueryFactory(em);
List<Area> areas = queryFactory.selectFrom(qArea)
.innerJoin(qAtlas)
.where(
qArea.name.containsIgnoreCase(name),
qAtlas.country.containsIgnoreCase(country),
qAtlas.department.containsIgnoreCase(department),
qAtlas.region.containsIgnoreCase(region)
)
.fetch();
long total = ( long ) areas.size( );
return new PageImpl(areas, PageRequest.of( page, size ), total );
}
目前控制台显示:
ervlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'atlas.areas' [select area
from org.thibaut.wheretoclimb.model.entity.Area area
inner join atlas.areas as area
where lower(area.name) like ?1 escape '!' and lower(atlas.country) like ?2 escape '!' and lower(atlas.department) like ?1 escape '!' and lower(atlas.region) like ?1 escape '!']] with root cause
org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'atlas.areas' [select area
from org.thibaut.wheretoclimb.model.entity.Area area
inner join atlas.areas as area
where lower(area.name) like ?1 escape '!' and lower(atlas.country) like ?2 escape '!' and lower(atlas.department) like ?1 escape '!' and lower(atlas.region) like ?1 escape '!']
有人可以帮我解决这个问题吗?
非常感谢你们的宝贵时间!
解决方案:
public Page< Area > searchAreaByNameAndCountryAndRegionAndDepartment( int page, int size, String name, String country, String region, String department ){
QAtlas qAtlas = QAtlas.atlas;
QArea qArea = QArea.area;
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
BooleanBuilder booleanBuilder = new BooleanBuilder();
if( ! name.equals( "" ) ){
booleanBuilder.and( qArea.name.containsIgnoreCase(name) );
}
if( ! country.equals( "" ) ){
booleanBuilder.and( qAtlas.country.containsIgnoreCase(country) );
}
if( ! region.equals( "" ) ){
booleanBuilder.and( qAtlas.region.containsIgnoreCase(region) );
}
if( ! department.equals( "" ) ){
booleanBuilder.and( qAtlas.department.containsIgnoreCase(department) );
}
List<Area> areas = queryFactory.from(qAtlas)
.innerJoin(qAtlas.areas, qArea)
.where(booleanBuilder)
.select(qArea)
.fetch();
long total = ( long ) areas.size( );
return new PageImpl(areas, PageRequest.of( page, size ), total );
}
非常感谢你们的宝贵时间!
我确切地说,我是 Java 开发人员一年级的法国学生。 而且我很确定我的问题标题对于我正在尝试做的事情甚至都不正确。那我希望下面的解释会更清楚!
我正在开发一个 multi-module 小应用程序,使用:Spring 启动、Spring 安全、Hibernate、Spring 数据、Spring MVC 和 Thymeleaf .
这里是my repo
我目前正在开发一种方法来处理 multi-criteria 请求。 我正在使用 Querydsl。
这里是涉及的部分域:
母实体
@Entity
@Table(name="element")
@Inheritance(strategy = InheritanceType.JOINED)
@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public abstract class Element {
//----------ATTRIBUTES----------
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@NotNull
@Size(min=4,max=50)
private String name;
private LocalDateTime createDate;
private LocalDateTime updateDate;
@OneToMany/*(mappedBy = "element")*/
@JoinColumn(name = "element_id")
private List< Comment > comments;
//..
child 个实体
/**
* Bean used to define a book or a document that contains one or many climbing areas
*/
@Entity
@Table(name="atlas")
@PrimaryKeyJoinColumn(name = "element_id")
@Getter
@Setter
@NoArgsConstructor
@ToString( exclude = {"areas" , "bookingRequests" , "user"})
public class Atlas extends Element {
//----------ATTRIBUTES----------
@ManyToMany
@JoinTable(
name = "areas_in_atlases",
joinColumns = { @JoinColumn(name = "atlas_id") },
inverseJoinColumns = { @JoinColumn(name = "area_id") } )
private List< Area > areas;
private String scale;
private String country;
private String region;
private String department;
private boolean available;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "atlas", cascade = CascadeType.ALL)
private List<BookingRequest> bookingRequests;
//..
/**
* Bean used to define an area with one or many climbing crag
*/
@Entity
@Table(name="area")
@PrimaryKeyJoinColumn(name = "element_id")
@Getter
@Setter
@NoArgsConstructor
@ToString
@EqualsAndHashCode( callSuper = true )
public class Area extends Element {
//----------ATTRIBUTES----------
@ManyToMany(mappedBy = "areas")
private List< Atlas > atlases;
private Atlas atlas;
@OneToMany/*(mappedBy = "area", fetch = FetchType.LAZY)*/
@JoinColumn(name = "area_id")
private List< Crag > crags;
private int approachDuration;
private String locality;
@OneToMany/*(mappedBy = "area", fetch = FetchType.LAZY)*/
@JoinColumn(name = "area_id")
private List< Parking > parking;
private String rockType;
//..
我的 multi-criteria 方法的第一部分正在运行(我猜不是很优雅......):
@Override
public Page<Atlas> searchAtlasByNameAndCountryAndRegionAndDepartment( int page, int size, String name, String country, String region, String department ){
QAtlas qAtlas = QAtlas.atlas;
ArrayList< Predicate > predicates = new ArrayList<>();
List<Atlas> atlases;
if( !name.equals( "" ) ){
predicates.add( qAtlas.name.containsIgnoreCase(name) );
}
if( !country.equals( "" ) ){
predicates.add( qAtlas.country.containsIgnoreCase(country) );
}
if( !region.equals( "" ) ){
predicates.add( qAtlas.region.containsIgnoreCase(region) );
}
if( !department.equals( "" ) ){
predicates.add( qAtlas.department.containsIgnoreCase(department) );
}
BooleanBuilder booleanBuilder = new BooleanBuilder();
for ( Predicate predicate: predicates ) {
booleanBuilder.and(predicate);
}
atlases = ( List< Atlas > ) getDaoFactory().getAtlasRepository().findAll( booleanBuilder );
long total = ( long ) atlases.size( );
return new PageImpl(atlases, PageRequest.of( page, size ), total );
}
但我坚持使用在 Area 中搜索的方法,使用仅在 Atlas 中的字段,例如 Country。
@Override
public Page< Area > searchAreaByNameAndCountryAndRegionAndDepartment( int page, int size, String name, String country, String region, String department ){
QAtlas qAtlas = QAtlas.atlas;
QArea qArea = QArea.area;
final JPAQueryFactory queryFactory = new JPAQueryFactory(em);
List<Area> areas = queryFactory.selectFrom(qArea)
.innerJoin(qAtlas)
.where(
qArea.name.containsIgnoreCase(name),
qAtlas.country.containsIgnoreCase(country),
qAtlas.department.containsIgnoreCase(department),
qAtlas.region.containsIgnoreCase(region)
)
.fetch();
long total = ( long ) areas.size( );
return new PageImpl(areas, PageRequest.of( page, size ), total );
}
目前控制台显示:
ervlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'atlas.areas' [select area
from org.thibaut.wheretoclimb.model.entity.Area area
inner join atlas.areas as area
where lower(area.name) like ?1 escape '!' and lower(atlas.country) like ?2 escape '!' and lower(atlas.department) like ?1 escape '!' and lower(atlas.region) like ?1 escape '!']] with root cause
org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'atlas.areas' [select area
from org.thibaut.wheretoclimb.model.entity.Area area
inner join atlas.areas as area
where lower(area.name) like ?1 escape '!' and lower(atlas.country) like ?2 escape '!' and lower(atlas.department) like ?1 escape '!' and lower(atlas.region) like ?1 escape '!']
有人可以帮我解决这个问题吗?
非常感谢你们的宝贵时间!
解决方案:
public Page< Area > searchAreaByNameAndCountryAndRegionAndDepartment( int page, int size, String name, String country, String region, String department ){
QAtlas qAtlas = QAtlas.atlas;
QArea qArea = QArea.area;
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
BooleanBuilder booleanBuilder = new BooleanBuilder();
if( ! name.equals( "" ) ){
booleanBuilder.and( qArea.name.containsIgnoreCase(name) );
}
if( ! country.equals( "" ) ){
booleanBuilder.and( qAtlas.country.containsIgnoreCase(country) );
}
if( ! region.equals( "" ) ){
booleanBuilder.and( qAtlas.region.containsIgnoreCase(region) );
}
if( ! department.equals( "" ) ){
booleanBuilder.and( qAtlas.department.containsIgnoreCase(department) );
}
List<Area> areas = queryFactory.from(qAtlas)
.innerJoin(qAtlas.areas, qArea)
.where(booleanBuilder)
.select(qArea)
.fetch();
long total = ( long ) areas.size( );
return new PageImpl(areas, PageRequest.of( page, size ), total );
}
非常感谢你们的宝贵时间!