하이버네이트 매핑 기본값은 Collection을 Lazy Fetching하게 되어 있다. 이걸 Lazy Collection Fetching이라고 하는데 일반적인 경우에는 매우 유용한 방법이다.

A -> C

A라는 클래스에서 B 타입의 Collection을 가지고 있다는 것을 이렇게 표기하겠다.

Member -> C Member -> C Member -> C

Member가 여러 컬렉션을 가지고 있다고 할 때 만약 하이버네이트나 다른 ORM 기술을 사용해 Member 객체를 읽어올 때 Member가 연관 관계를 맺고 있는 모든 컬렉션까지 전부 다 가져온다고 생각해보자.

메모리와 실행 시간이 엄청 오래 걸릴 것이다. 잘하면(?) 데이터베이스에 들어있는 모든 데이터를 한방에 메모리로 올리는 상황도 연출될 수 있다.

그래서 Lazy Collection Fetching같은게 필요하다. 딱 Member 객체만 가져오고 C, C, C 같은건 실제 해당 컬렉션에 접근할 때 쿼리가 발생한다.

문제 상황을 연출해봤다.

[java]
public void generateTestData() {
List<Integer> idList = new ArrayList<Integer>();

for (int i = 0; i < 100; i++) {
Member member = new Member();
member.setName(i + "keesun");
member.setEmail("keesun" + i + "@mail.com");
getSession().save(member);
idList.add(member.getId());
}

for (int j = 0; j < 10000; j++) {
Item item = new Item();
item.setName(j + "items");

int randomId = (int) (Math.random() * 100);
Member owner = (Member) getSession().load(Member.class, idList.get(randomId));

item.setOwner(owner);
getSession().save(item);
}
}
[/java]

랜덤으로 Member 객체 100개와 Item 객체 10000개를 조합했다.

이제 Item을 가장 많이 가지고 있는 Member 정보와 해당 Item의 갯수를 알고 싶다고 해보자.

[java]
@Test
public void membeItemWithNPlus1UsingCriteria(){
int mostItemCount = 0;
Member mostItemOwner = null;

List<Member> memberList = getSession().createCriteria(Member.class).list();
for (Member thisMember : memberList) {
Set<Item> memberItems = thisMember.getItems();
if(mostItemCount < memberItems.size()) {
mostItemCount = memberItems.size();
mostItemOwner = thisMember;
}
}

System.out.println(mostItemCount);
System.out.println(mostItemOwner);
}
[/java]

이때 발생한 쿼리는 다음과 같다.

Criteria를 사용한 아주 간단한 쿼리로 모든 Member 객체를 가져온 다음 Memebr를 순회하며 각 Member의 C 크기를 비교하고 있다. 첫번째 쿼리는 Member를 가져올 때 발생하고 그 다음 N번의 쿼리는 member.getItems()를 호출할 때 발생하게 된다. 이 방법은 select 문이 너무 많이 발생하기 때문에 DB에 부하를 줄 수 있어 보통 N+1 Select Problem이라고 한다.

보통 하이버네이트를 사용했을 때 뭔가 무척 느리다고 느껴진다면 이런 현상이 발생하는 부분이 없는지 확인해봐야 한다. 그리고 해결책을 적용하면 된다. 하이버네이트는 이미 N+1 Select 문제를 해결할 수 있는 여러가지 방법을 제공한다. 앞으로 그것들을 하나씩 살펴볼 계획이다.