Spring Data JPA 같은 이름, 다른 type인 2개의 @Entity인 경우 주의 사항

2020. 9. 4. 16:24 Spring Framework/Spring boot

가정

  • 하나의 project에 의미가 다른 2개의 Event라는 @Entity가 필요하다.
  • 당연히 package 경로는 다르다.
    • 하나는 net.woniper.data.jpa.event1.Event (이하 event1)
    • 하나는 net.woniper.data.jpa.event2.Event (이하 event2)
    • package만 다르며, 클래스 명은 같다.
  • 각각의 Event는 Repository가 존재한다.
    • net.woniper.data.jpa.event1.EventRepository (이하 eventRepository1)
    • net.woniper.data.jpa.event2.EventRepository (이하 eventRepository2)

예제 코드

net.woniper.data.jpa.event1.Event

package net.woniper.data.jpa.event1;

@Entity
@Table(name = "event1")
@NoArgsConstructor
@ToString
public class Event {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    public Event(String name) {
        this.name = name;
    }
}

net.woniper.data.jpa.event1.EventRepository

package net.woniper.data.jpa.event1;

public interface EventRepository extends CrudRepository<Event, Long> {

    List<Event> findByOrderByNameDesc();
}

net.woniper.data.jpa.event2.Event

package net.woniper.data.jpa.event2;

@Entity
@Table(name = "event2")
@NoArgsConstructor
@ToString
public class Event {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    public Event(String name) {
        this.name = name;
    }
}

net.woniper.data.jpa.event2.EventRepository

package net.woniper.data.jpa.event2;

public interface EventRepository extends CrudRepository<Event, Long> {}

위 예제는 package 경로만 다를 뿐 eventRepository1에 findByOrderByNameDesc 메소드를 제외하고 클래스명, 코드 모두 같다.

문제1

예제를 작성한 후 먼저 application을 실행하면 처음 맞이하는 문제가 있다.

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'net.woniper.data.jpa.event1.EventRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

EventRepository Bean으로 등록되는 시점에 bean name을 EventRepository의 camel case인 eventRepository로 bean name이 등록된다. 그런데 EventRepository 라는 이름의 Bean 대상이 2개이기 때문에 충돌이 난다.

해결 방법

package net.woniper.data.jpa.event2;

@Repository("event2Repository")
public interface EventRepository extends CrudRepository<Event, Long> {

    List<Event> findByOrderByNameDesc();
}

eventRepository2에 bean name을 event2Repository로 설정했다. 문제 해결!

문제2

net.woniper.data.jpa.event1.EventRepository.findByOrderByNameDesc 메소드 호출 후 실행되는 쿼리를 보자.


select event0_.id as id1_1_, event0_.name as name2_1_ from event2 event0_ order by event0_.name desc

JPA에 의해 생성/실행된 쿼리기 때문에 alias가 조금 보기 힘들지만, 잘 보면 조회한 테이블은 event1인데, 실행된 쿼리의 테이블이 event2 테이블이다. 어떻게 된걸까?

해결 방법

@Entity(name = "event1")
@Table(name = "event1")
public class Event {}

@Entity(name = "event2")
@Table(name = "event2")
public class Event {}

@Entity의 name 속성을 정의하자. @Entity#name 속성은 default로 클래스 명으로 설정된다. 패키지 명까지 포함되는 게 아니라 클래스 명으로만 name 속성이 기본 정의되기 때문에 문제가 된다. 왜 문제가 되는걸까?

JPA의 쿼리 생성/조회

JPA는 자동으로 쿼리를 만들어준다. 심지어 Spring Data JPA는 QueryMethod라는 기능을 사용해 메소드만으로 쿼리를 만들 수 있는데, 자동으로 쿼리를 생성할때 테이블명을 @Entity#name 속성으로 생성한다. 아래와 같이 말이다.

SELECT * FROM {#entityName} x;

이 내용에 대해서는 Spring Data JPA 문서를 참고하자.

출처 : https://blog.woniper.net/359?category=699184