본문 바로가기

Spring

Querydsl에서 Cross Join이 발생할 경우

회사에서 Querydsl를 사용하던 중 DB결과가 느려 쿼리문을 확인해보니 Cross Join이 계속해서 발생하는 것을 확인하게 되었고 이를 해결하면서 얻은 정보를 작성한 글입니다.

 

 

JPA 기반의 환경에서 Querydsl을 사용하여 Join 쿼리 작성시 Cross Join이 발생할 수 있습니다.

Cross Join의 결과는 2개의 집합의생길 수 있는 모든 데이터의 조합 (M*N) 이 나오는 경우를 이야기 합니다.

해당 결과는 Inner Join보다 성능상 이슈가 발생하게 됩니다.

어떤 경우에 Cross Join이 발생하는지, 해결할 수 있는지 확인해보겠습니다.

 

1. 테스트 세팅

테스트를 위한 Entity

User

@Getter
@Entity
@Table(name = "member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "user_id")
	private Long id;

	@Column(nullable = false)
	private String email;

	@Column
	private String password;

	@Column
	private String name;

	@ManyToOne
	@JoinColumn(name = "team_id")
	private Team team;

}

Team

@Entity
@Getter
@NoArgsConstructor
public class Team {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column
	private Long id;

	@Column
	private String name;
}

User와 Team은 ManyToOne 관계인 상황입니다.

2. 테스트

사용할 Querydsl 코드는 다음과 같습니다.

@Override
	public List<User> getJoinTest() {
		return queryFactory.selectFrom(user)
			.where(user.name.eq(team.name))
			.fetch();
	}

해당 코드를 실행할 경우 Cross Join이 발생하는 것을 확인할 수 있습니다.

현재 코드에서는 별도의 join절이 선언되어 있지 않고 where 문에서 바로 연관관계를 사용했기 때문에 Cross Join이 발생합니다.

Querydsl-JPA에서 사용하는 Hibernate의 특징 때문에 발생합니다.

  • Hibernate에서 암묵적인 Join은 Cross Join을 사용한다.

해당 이슈는 Querydsl를 사용하는 것이 아닌 @Query를 사용하는 경우에도 발생합니다.

해결책

해결방법은 암묵적 Join에서 명시적 Join으로 변경하는 것입니다.

@Override
	public List<User> getJoinTest() {
		return queryFactory.selectFrom(user)
			.innerJoin(user.team, team)
			.where(user.name.eq(team.name))
			.fetch();
	}

.innerJoin(user.team, team)을 추가하여 명시적으로 Join을 선언해줍니다.

명시적 Join을 사용했기 때문에 Cross Join이 아닌 Inner Join이 발생하였습니다.