Querydsl: leftJoin(target).on(condition)์ด ์žˆ๋Š” ์™ผ์ชฝ ์กฐ์ธ "๊ฒฝ๋กœ๊ฐ€ ์กฐ์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค! ์˜ค๋ฅ˜

์— ๋งŒ๋“  2016๋…„ 01์›” 14์ผ  ยท  8์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: querydsl/querydsl

์•ˆ๋…•ํ•˜์„ธ์š”,

leftJoin(EntityPath<P> target).on(Predicate condition) ํ‘œ๊ธฐ๋ฒ•์œผ๋กœ ์™ผ์ชฝ ์กฐ์ธ์„ ์ˆ˜ํ–‰ํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ๋‚ด ์—”ํ‹ฐํ‹ฐ์ž…๋‹ˆ๋‹ค.

@Entity(name = "CLASSROOM")
public class Classroom {
    // [...]
}

@Entity(name = "STUDENT")
public class Student {

    @Column(name = "NAME")
    private String name;

    <strong i="9">@ManyToOne</strong>
    @JoinColumn(name = "CLASSROOM_ID")
    private Classroom classroom;

    // [...]
}

๋‹ค์Œ์€ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์ฟผ๋ฆฌ์˜ ์˜ˆ์ž…๋‹ˆ๋‹ค.

QClassroom classroom = QClassroom.classroom;
QStudent student = QStudent.student;

query.from(classroom)
    .leftJoin(student).on(classroom.eq(student.classroom))
    .where(student.name.eq("antoine"))
    .list(classroom)

๋‹ค์Œ์€ ์˜ค๋ฅ˜ ๋กœ๊ทธ์ž…๋‹ˆ๋‹ค.

org.hibernate.hql.internal.ast.QuerySyntaxException: Path expected for join! [select classroom
from com.my.personnal.path.entity.Classroom classroom
  left join STUDENT student with classroom = student.classroom
where student.name = ?1]

๋ฌผ๋ก  ์ด ์˜ˆ์—์„œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

query.from(student)
    .leftJoin(student.classroom, classroom)
    .where(student.name.eq("antoine"))
    .list(classroom)

...ํ•˜์ง€๋งŒ ๋‚ด ์‹ค์ œ ์ฟผ๋ฆฌ๋Š” ์ด๋ณด๋‹ค ๋” ์ปค์„œ query.from(student) ์ฟผ๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋‚ด๊ฐ€ ๋ญ˜ ์ž˜๋ชปํ•˜๊ณ  ์žˆ์–ด?

QueryDsl 3.6.6์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

resolved

๋ชจ๋“  8 ๋Œ“๊ธ€

๊ทธ๊ฒƒ์€ํ•ด์•ผํ•œ๋‹ค

QClassroom classroom = QClassroom.classroom;
QStudent student = QStudent.student;

query.from(classroom)
    .leftJoin(classroom.students, student)
    .where(student.name.eq("antoine"))
    .list(classroom)

์ด๋ฅผ ์œ„ํ•ด ๊ต์‹ค์—์„œ ํ•™์ƒ๋“ค์„ ์—ญ์œผ๋กœ ๋งคํ•‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋„ค, ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํ•ด๊ฒฐ์ฑ…์ด ๋  ์ˆ˜ ์žˆ์ง€๋งŒ ์‹ค์ œ๋กœ๋Š” ๋‘ ๊ฐ€์ง€ ์ œ์•ฝ ์กฐ๊ฑด์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • .from(classroom) ์ฟผ๋ฆฌ๋ฅผ ์‹œ์ž‘ํ•˜๋ ค๋ฉด
  • Student ์—”ํ‹ฐํ‹ฐ์—์„œ ManyToOne ์—ฐ๊ด€์ด ์žˆ๋Š” ๋‘ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์—ฐ๊ฒฐํ•˜๋ ค๋ฉด(์–‘๋ฐฉํ–ฅ ์—ฐ๊ด€ ์—†์ด)

์ด ๋‘ ๊ฐ€์ง€ ์ œ์•ฝ ์กฐ๊ฑด์—์„œ ๋‚ด ๋‘ ์—”ํ„ฐํ‹ฐ๋ฅผ ๊ฒฐํ•ฉํ•  ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Šต๋‹ˆ๊นŒ?

์ด๊ฒƒ์€ ๋˜ํ•œ ์ž‘๋™ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค

query.from(student, classroom)
    .where(
        student.classroom.eq(classroom),
        student.name.eq("antoine"))
    .list(classroom)

๊ทธ๋ž˜๋„ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์œผ๋ฉด ๋‹ค์‹œ ์—ด์–ด์ฃผ์„ธ์š”

์•ˆ๋…•ํ•˜์„ธ์š” ํ‹ฐ๋ชจ๋‹˜

์ด ๋ฌธ์ œ๋ฅผ ๋‹ค์‹œ ์—ด์–ด์„œ ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ๋น„์Šทํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด ์œ„์—์„œ ์„ค๋ช…ํ•œ ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

๋‚ด ์Šคํ‚ค๋งˆ:
์บ ํผ์Šค.ํด๋ž˜์Šค

<strong i="9">@Entity</strong>
public class Campus {

<strong i="10">@Id</strong>
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String name;

@OneToMany(mappedBy = "associatedCampus", cascade = CascadeType.ALL)
private Set<Building> buildings;

... Getters/Setters, Constructors

๋นŒ๋”ฉ ํด๋ž˜์Šค

<strong i="14">@Entity</strong>
public class Building {

<strong i="15">@Id</strong>
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
private String uuid;

<strong i="16">@ManyToOne</strong>
@JoinColumn(name = "associatedCampus")
private Campus associatedCampus;

... Getters/Setters, Constructors

Campus ํ…Œ์ด๋ธ”์—์„œ Building ํ…Œ์ด๋ธ”๋กœ ์กฐ์ธํ•˜๋ ค๊ณ  ํ•˜์ง€๋งŒ ํ•ญ์ƒ ์ดํ•ดํ•  ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
์˜ค๋ฅ˜: java.lang.IllegalArgumentException: building.associatedCampus๋Š” ๋ฃจํŠธ ๊ฒฝ๋กœ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

๊ฑด๋ฌผ์—์„œ ์บ ํผ์Šค๋กœ ํ•ฉ๋ฅ˜ํ•˜๋Š” ๊ฒƒ์€ ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ์—†์ง€๋งŒ ์บ ํผ์Šค์—์„œ ๊ฑด๋ฌผ๋กœ ํ•ฉ๋ฅ˜ํ•˜๋Š” ๊ฒƒ์€ ์ „ํ˜€ ๋ฌธ์ œ๊ฐ€ ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
์˜ˆ์‹œ:

public List<Building> getBuildingsByCampusName(String campusName) {
  JPAQuery<Building> query = new JPAQuery<>(em);
  QCampus campus = QCampus.campus;
  QBuilding building = QBuilding.building;
  query.select(building)
  .from(building)
  .join(building.associatedCampus, campus)
  .where(campus.name.like("%" + campusName + "%"));
  List<Building> result = query.fetch();
  return result;

๋‚ด ๋ฌธ์ œ๊ฐ€ ๋ฌด์—‡์ธ์ง€ ๋˜๋Š” ๊ฑด๋ฌผ->์บ ํผ์Šค์˜ ์—ญ์กฐ์ธ์ด ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ์•Œ๋ ค์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

๊ทธ๋ฆฌ์ธ 
๋งˆ์ปค์Šค

๋‚ด ์—”ํ‹ฐํ‹ฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
@์‹ค์žฌ
@Table(์ด๋ฆ„ = "์ฐธ๊ฐ€์ž")
๊ณต๊ฐœ ์ˆ˜์—… ์ฐธ๊ฐ€์ž {
@OneToOne(์บ์Šค์ผ€์ด๋“œ = CascadeType.DETACH, ๊ฐ€์ ธ์˜ค๊ธฐ = FetchType.EAGER, targetEntity = ServiceType.class)
@JoinTable(์ด๋ฆ„ = "PARTICIPANT_SERVICES", joinColumns = @JoinColumn(์ด๋ฆ„ = "PARTICIPANT_ID", referencedColumnName = "PARTICIPANT_ID"), inverseJoinColumns = @JoinColumn(์ด๋ฆ„ = "SERVICE_TYPE_ID", referencedColumnName = "SERVICE_TYPE_ID"))
๊ฐœ์ธ ๋ชฉ๋ก์„œ๋น„์Šค ์œ ํ˜•;

    @OneToMany(mappedBy = "participant")
@Cascade({org.hibernate.annotations.CascadeType.ALL})
private List<ParticipantRegionCountry> participantRegionCountries;

๊ฒŒํ„ฐ/์„ธํ„ฐ
}

@์‹ค์žฌ
@Table(์ด๋ฆ„ = "SERVICE_TYPE")
๊ณต๊ฐœ ํด๋ž˜์Šค ์„œ๋น„์Šค ์œ ํ˜• {

<strong i="20">@Id</strong>
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "SERVICE_TYPE_ID")
private Long id;

@Column(name = "SERVICE_TYPE_NAME")
private String name;
   getter / setter

}
์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด์„œ ์ฐธ๊ฐ€์ž ์—”ํ„ฐํ‹ฐ๋ฅผ ์ฟผ๋ฆฌํ•  ๋•Œ:
๋ชฉ๋ก์ฐธ๊ฐ€์ž = ์ƒˆ๋กœ์šด ArrayList<>();

    JPQLQuery<Participant> query = new JPAQuery<>(em);
    QParticipant participant = QParticipant.participant;
    QParticipantRegionCountry participantRegionCountry = QParticipantRegionCountry.participantRegionCountry;
    BooleanBuilder predicate = new BooleanBuilder();
    predicate.and(participantRegionCountry.region.in(regions));

    participants = query.select(participant).from(participant).join(participant.participantRegionCountries, participantRegionCountry).where(predicate).distinct().fetch();

์˜ˆ์™ธ:
์ตœ๋Œ€ ์ ˆ์ „ ๋ชจ๋“œ : CONTACT_NAME5_6_๋ฟ๋งŒ participan0_.CONTACT_PHONE๋กœ PARTICIPANT_ID1_6_, BUSINESS_NAME2_6_ ๊ฐ™์€ participan0_.BUSINESS_NAME, BUSINESS_TYPE_ID9_6_ ๊ฐ™์€ participan0_.BUSINESS_TYPE_ID, CITY_ID10_6_ ๊ฐ™์€ participan0_.CITY_ID, CONTACT_ADDRESS3_6_ ๊ฐ™์€ participan0_.CONTACT_ADDRESS, CONTACT_EMAIL4_6_ ๊ฐ™์€ participan0_.CONTACT_EMAIL, participan0_.CONTACT_NAME๋กœ ๊ตฌ๋ถ„ participan0_.PARTICIPANT_ID์„ ์„ ํƒ CONTACT_PHONE6_6_, participan0_.COUNTRY_ID REGION_ID17_6_ ๊ฐ™์€ COUNTRY_ID11_6_, participan0_.CURRENCY_ID CURRENCY_ID12_6_๋กœ์„œ participan0_.LANGUAGE_ID LANGUAGE_ID13_6_๋กœ์„œ participan0_.PARTICIPANT_PACK_ID PARTICIPANT_PACK_14_6_๋กœ์„œ participan0_.PARTICIPANT_TYPE_ID PARTICIPANT_TYPE_15_6_๋กœ์„œ participan0_.PAYMENT_METHOD_TYPE_ID PAYMENT_METHOD_TY16_6_๋กœ์„œ participan0_.PROJECTED_VOLUME PROJECTED_VOLUME7_6_๋กœ์„œ participan0_.REGION_ID๋กœ์„œ participan0_.USER_NAME์„ USER_NAME8_6_์œผ๋กœ, participan0_1_.SERVICE_TYPE_ID๋ฅผ SERVICE_TYPE_ID1_12_๋กœ PARTICIPANT participan0_ ์™ผ์ชฝ ์™ธ๋ถ€ ์กฐ์ธ PARTICIPANT_SERVICES par ticipan0_1_ on participan0_.PARTICIPANT_ID=participan0_1_.PARTICIPANT_ID ๋‚ด๋ถ€ ์กฐ์ธ SERVICE_TYPE servicetyp1_ on participan0_1_.SERVICE_TYPE_ID=servicetyp1_.SERVICE_TYPE_ID ๋‚ด๋ถ€ ์กฐ์ธ ๋ฐ ((? , ?)์˜ ์ฐธ๊ฐ€์ž2_.REGION_ID)
์ตœ๋Œ€ ์ ˆ์ „ ๋ชจ๋“œ: servicetyp0_.SERVICE_TYPE_ID๋ฅผ SERVICE_TYPE_ID1_18_0_๋กœ ์„ ํƒํ•˜๊ณ  servicetyp0_.SERVICE_TYPE_NAME์„ SERVICE_TYPE_NAME2_18_0_์œผ๋กœ SERVICE_TYPE servicetyp0_์—์„œ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ servicetyp0_.SERVICE_TYPE_ID=?
javax.persistence.PersistenceException: org.hibernate.PropertyAccessException: ํ•„๋“œ ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์—†์Œ [com.mycompany.app.spring.orm.model. ServiceType@4e9e0c6 ] ๋ฐ˜์‚ฌ์— ์˜ํ•œ ๊ฐ’: [class com.mycompany.app.spring.orm.model.Participant.serviceTypes] com.mycompany.app.spring.orm.model.Participant.serviceTypes์˜ setter
org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:149)์—์„œ
org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157)์—์„œ
org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1423)์—์„œ
org.hibernate.query.Query.getResultList(Query.java:146)์—์„œ
sun.reflect.NativeMethodAccessorImpl.invoke0(๋„ค์ดํ‹ฐ๋ธŒ ๋ฉ”์„œ๋“œ)์—์„œ
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)์—์„œ
sun.reflect.DelegatingMethodAccessorImpl.invoke์—์„œ(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:497)์—์„œ
org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:379)
com.sun.proxy.$Proxy47.getResultList์—์„œ(์•Œ ์ˆ˜ ์—†๋Š” ์†Œ์Šค)
com.querydsl.jpa.impl.AbstractJPAQuery.getResultList(AbstractJPAQuery.java:160)
com.querydsl.jpa.impl.AbstractJPAQuery.fetch(AbstractJPAQuery.java:202)
com.mycompany.app.spring.orm.service.ParticipantService.listParticipants(ParticipantService.java:53)
com.mycompany.app.App.main(App.java:56)
์›์ธ: org.hibernate.PropertyAccessException: ํ•„๋“œ ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜ ์—†์Œ [com.mycompany.app.spring.orm.model. ServiceType@4e9e0c6 ] ๋ฐ˜์‚ฌ์— ์˜ํ•œ ๊ฐ’: [class com.mycompany.app.spring.orm.model.Participant.serviceTypes] com.mycompany.app.spring.orm.model.Participant.serviceTypes์˜ setter
org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:61)์—์„œ
org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:649)์—์„œ
org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:205)์—์„œ
org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:4711)์—์„œ
org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:183)์—์„œ
org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:125)์—์„œ
org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:1152)์—์„œ
org.hibernate.loader.Loader.processResultSet(Loader.java:1011)์—์„œ
org.hibernate.loader.Loader.doQuery(Loader.java:949)์—์„œ
org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:341)์—์„œ
org.hibernate.loader.Loader.doList(Loader.java:2692)์—์„œ
org.hibernate.loader.Loader.doList(Loader.java:2675)์—์„œ
org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2507)์—์„œ
org.hibernate.loader.Loader.list(Loader.java:2502)์—์„œ
org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:502)์—์„œ
org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:392)
org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:216)
org.hibernate.internal.SessionImpl.list(SessionImpl.java:1490)์—์„œ
org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1445)
org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1414)
... 11๊ฐœ ๋”
์›์ธ: java.lang.IllegalArgumentException: java.util.List ํ•„๋“œ com.mycompany.app.spring.orm.model.Participant.serviceTypes๋ฅผ com.mycompany.app.spring.orm.model.ServiceType์œผ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)์—์„œ
sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)์—์„œ
sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)์—์„œ
java.lang.reflect.Field.set(Field.java:764)์—์„œ
org.hibernate.property.access.spi.SetterFieldImpl.set(SetterFieldImpl.java:41)์—์„œ
... 30๊ฐœ ๋”
์–ด๋–ค ํ•ด๊ฒฐ์ฑ…์ด ์žˆ์Šต๋‹ˆ๊นŒ?
QueryDSL์„ ์ฒ˜์Œ ์ ‘ํ–ˆ๋Š”๋ฐ ์™œ service_types์—์„œ ์ฟผ๋ฆฌํ•ฉ๋‹ˆ๊นŒ?

๊ทธ๋Ÿฌ๋ฉด ์ž์ฒด ์กฐ์ธ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•ด์•ผ ํ•ฉ๋‹ˆ๊นŒ?

์ด ๊ฒฝ์šฐ์—๋Š” ํ™•์‹คํžˆ ์ •์˜๋œ ๊ด€๊ณ„๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

JPQL์€ ์†์„ฑ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฏ€๋กœ ์†์„ฑ์œผ๋กœ ์ •์˜๋œ ๊ด€๊ณ„๊ฐ€ ์—†์œผ๋ฉด ์‹ค์ œ๋กœ ์ž„์˜๋กœ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๋” ๋งŽ์€ SQL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” JpaSqlQuery๋กœ ์ด๋™ํ•˜์ง€ ์•Š๋Š” ํ•œ.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰