8.2.4. HQL 공부하기 – inner join

insertDatas() 메소드에서 집어 넣는 데이타에서 Member와 Messenger의 모습을 보면 다음과 같습니다.

사용자 삽입 이미지

seal 멤버만 두개의 Messenger 정보를 가지고 있습니다. 이 때 inner join을 하면 다음과 같이 두개의 레코드가 생기게 됩니다.
사용자 삽입 이미지inner join을 HQL로 하는 방법은 s.create(“from Member m inner join m.messengers”) 이렇게 콜렉션을 가리키면 됩니다.



public void testJoinHQL(){


       insertDatas();


       q = s.createQuery(“from k_Member m inner join m.messengers”);


       List<Object> result = q.list();


       // Member m1 | Messenger msg1(“keesun”, MSN)


       // Member m1 | Messenger msg2(“keesun2”, Skype)


       assertEquals(2, result.size());


       Object[] result1 = (Object[]) result.get(0);


       assertTrue(result1[0] instanceof KMember);


       assertTrue(result1[1] instanceof KMessenger);


       KMessenger msg1 = (KMessenger) result1[1];


       assertEquals(“seal”, msg1.getM_id());


       assertEquals(KMessengerType.MSN, msg1.getM_type());


}

8.2.3. HQL 공부하기 – order by절



public void testOrderByHQL(){


       insertDatas();


       q = s.createQuery(“select m.name from k_Member m”);


       List<String> names1 = q.list();


       StringBuffer sb = new StringBuffer();


       for(String name : names1)


             sb.append(name);


       assertEquals(“sealparadozzkeesun”, sb.toString());


 


       q = s.createQuery(“select m.name from k_Member m order by m.name”);


       List<String> names2 = q.list();


       assertEquals(3, names2.size());


       assertEquals(“keesun”, names2.get(0));


       assertEquals(“paradozz”, names2.get(1));


       assertEquals(“seal”, names2.get(2));


}


위에 있는 테스트 코드를 보시면 원래 테이블에 들어있는 이름의 순서는 seal -> paradozz -> keesun 이였는데 두번째 쿼리에서 order by를 사용하고 보니 keesun이 첫번째로 나오게 되는 것을 확인할 수 있습니다.

8.2.2. HQL 공부하기 – where절

가장 간단하게 Where 절을 사용한 HQL은 다음과 같습니다.
s.createQuery(“from Member m where m.name = ‘기선'”);

위에서 ‘기선’과 같은 부분을 파라미터화 하는 방법에는 두 가지가 있습니다.
1. ? 를 사용하는 방법과
2. :를 사용하는 방법이 있습니다.

1. s.createQuery(“from Member m where m.name = ? “); 이렇게 파라미터화 할 부분을 ? 로 표시하고 fluent interface 형태로 구현해 놓았기 때문에 뒤에 연달아서 .setParameter(0, “기선”); 을 덧 붙여 주면 됩니다.

2. s.createQuery(“from Member m where m.name = :name “); 이렇게 :뒤에 변수 처럼 사용할 이름을 적어주고 .setParameter(“name”, “기선”); 이렇게 덧붙여 주면 됩니다. 파라미터가 여러개라면 이렇게 이름을 줘서 사용하는 것이 가독성이 좋을 듯 합니다.
이 때 파라미터의 타입을 명확히 주고 싶다면 setString과 같은 메소드를 setParameter대신에 사용하면 됩니다.

3. and 를 사용해서 여러 개의 조건을 줄 수도 있습니다.
이 때 여러 개의 조건을 줄 때 쿼리 결과를 최소한으로 줄여주는 조건문을 앞에 두는 것이 좋습니다.



public void testWhereHQL(){


       insertDatas();


       q = s.createQuery(“select m from k_Member m where name=’keesun'”);


       assertEquals(1, q.list().size());


       //1


       q = s.createQuery(“select m from k_Member m where name = ?”)


              .setParameter(0, “keesun”);


       assertEquals(1, q.list().size());


       //2


       q = s.createQuery(“select m from k_Member m where name = :name”)


              .setParameter(“name”, “keesun”);


       assertEquals(1, q.list().size());


       //3


       q = s.createQuery(“select m from k_Member m where name like :name and email like :email”)


              .setString(“name”, “%a%”)


              .setString(“email”, “%os.net”);


       assertEquals(2, q.list().size());


}

HQL과 @Entity에 있는 name 속성의 관계

ejb 3.0 spec 8장 제일 처음에 나오는 @Entity에 대한 설명을 찾아 보게 된 원인은 HQL을 작성하려다가 다음과 같은 형상이 벌어졌기 때문입니다.

@Entity
Member{}

이런 클래스가 있고 이 클래스에 대한 HQL을 작성할 때 다음과 같이 작성을 하면 에러가 발생합니다.

Session s;
s.createQuery(“from member“);

“” 안에는 HQL이라고 SQL과 비슷한 구문이 들어가지만 SQL은 테이블명과 컬럼명을 기준으로 작성하고 select가 있어야 하지만 HQL은 from만 필수 요소이고 클래스명과 멤버변수명으로 작성을 한다고 배웠습니다. 따라서 다음과 같이 작성해야 원하는 대로 작업이 수행됩니다.

s.createQuery(“from Member“);

문제는 @Entity의 속성중에 name 속성에 값을 줬을 때 발생합니다.

@Entity(name = “K_Member”)
KMember{}

이런 클래스가 있을 때 클래스명을 사용해서 다음과 같이 HQL을 만들려고 해보지만
 
s.createQuery(“from KMember“);

KMember is not Mapped라는 에러 메시지를 볼 수 있습니다.

s.createQuery(“from K_Member“);

이렇게 @Entity의 name 속성에 지정한 이름으로 작성해야 원하는 결과를 얻을 수 있습니다.

결과적으로 HQL에서 쿼리를 작성할 때 @Entity 어노테이션의 name 속성에 들어가는 값을 사용해야 하며 대소문자를 구분합니다. @Entity의 name에 지정해 주는 값은 DB에서 테이블 명이 되기도 하지만 일치 하지는 않습니다. postgres에서 확인해본 결과 DB에 들어간 테이블 명은 k_member와 같이 전부 소문자로 바뀝니다. 따라서 클래스명이 바뀐다고 생각을 하는 편이 더 이해하기 쉬울 것 같습니다.

물개 선생님께서 알려 주셔서 테스트를 @Column의 name 속성을 주고 HQL을 작성할 때 name 속성에 넣은 값을로도 해봤습니다. 하지만 멤버변수들은 @Column의 name에 넣어준 값에 영향을 받지 않습니다. 멤버변수명으로 해야 HQL Query Exception이 발생하지 않습니다.

bm223.pdfp157 에 간단하게 설명이 있습니다. @Entity의 name속성에 지정한 값은 쿼리에서 사용 되며 JPQL의 예약어 이면 안된다고 합니다.