Hibernate에서는 HQL에 익숙하지 못하거나 HQL 작성시 발생할 수 있는 오타로 인한 오류를 최소화 하기 위해
org.hibernate.Criteria API를 사용할 수 있도록 한다.
Criteria API 호출을 통해 특정 객체에 대한 조회가 가능하고
org.hibernate.criterion.Restrictions API 호출을 통해 WHERE문에 해당하는
기본 조회 조건을 정의할 수 있다.
기본적인 사용 방법
Hibernate Criteria를 이용하여 특정 객체 정보에 대해 조회할 수 있다.
Case 1. Basic
다음은 하나의 테이블을 대상으로 Criteria를 이용하여 조회를 수행하는 예이다.
Criteria criteria = session.createCriteria(Country.class);
criteria.add(Restrictions.like("countryName", "", MatchMode.ANYWHERE));
criteria.addOrder(Order.asc("countryName"));
List countryList = criteria.list();
대상이 되는 테이블과 매핑되는 클래스로 Criteria를 생성하고 Restriction API를 호출해 WHERE조건에 해당하는
조건절을 정의할 수 있다. 위와 같이 정의 한 경우
WHERE Country.countryName like '%%'와 같은 조건절이 생성된다.
또한 addOrder()를 통해 order by절을 정의할 수 있다.
이와 같이 Criteria API를 이용할 경우 메소드를 통해
검색 조건을 정의하기 때문에 오타로 인한 오류를 최소화할 수 있게 된다.
조회 조건을 정의하기 위한 org.hibernate.criterion.Restrictions 는 eq, gt, ge, isNull 등을 비롯하여 다양한 API를 제공하고 있다.
보다 자세한 내용을 알기 위해서는
여기
를 참고하도록 한다.
Case 2. Join
Relation 관계에 놓여 있는 두개의 테이블을 대상으로 Hibernate Criteria(Inner Join)를 이용한 조회 작업을 수행할 수 있다.
Criteria movieCriteria = session.createCriteria(Movie.class);
Criteria categoryCriteria = movieCriteria.createCriteria("categories");
categoryCriteria.add(Restrictions.eq("categoryName", "Romantic"));
List movieList = movieCriteria.list();
위 코드에서는 Movie 클래스와 Relation 관계에 놓인 Category를 Join하기 위해 각 Movie 객체에 해당하는
Criteria에 Category 객체에 해당하는 Criteria를 생성하고 있다.
여기서는 Restrictions API를 사용하여 categoryName = 'Romantic'인 결과값을 찾게될 것이다.
또한, Relation 관계에 놓여 있는 두개의 테이블을 대상으로 Hibernate Criteria(Left Outer Join)을 이용한 조회 작업을 수행할 수 있다.
Criteria categoryCriteria = session.createCriteria(Category.class);
Criteria movieCriteria = categoryCriteria.createCriteria("movies",
CriteriaSpecification.LEFT_JOIN);
categoryCriteria.addOrder(Order.asc("categoryName"));
categoryCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
List categoryList = categoryCriteria.list();
Relation 관계에 있는 테이블의 Criteria를 생성할 때 CriteriaSpecification을 통해 LEFT_JOIN, RIGHT_JOIN 등을 명시할수 있다.
또한 Criteria.DISTINCT_ROOT_ENTITY를 사용하면 List에 중복 포함된 루트 개체를 제거할 수 있다.
위에서 설명된 코드들은
HibernateBasicCriteriaTest.java
에서 확인할 수 있다.
원하는 객체 형태로 전달
Criteria의 setResultTransformer 메소드를 사용하여 Criteria를 이용한 조회 결과를 별도 정의한 객체 형태로 전달받을 수 있다.
Case 1. 특정 객체 형태로 전달
Relation 관계에 놓여 있는 두개의 테이블을 대상으로 Criteria를 이용한 조회 결과를 특정 객체인
Movie 객체 형태로 전달받을 수 있다.
Criteria movieCriteria = session.createCriteria(Movie.class);
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.id().as("movieId"));
projectionList.add(Projections.property("title").as("title"));
projectionList.add(Projections.property("director").as("director"));
movieCriteria.setProjection(projectionList);
movieCriteria.setResultTransformer(new AliasToBeanResultTransformer(
Movie.class));
Criteria categoryCriteria = movieCriteria.createCriteria("categories",
"category");
Criteria countryCriteria = movieCriteria.createCriteria("country",
"country");
categoryCriteria.add(Restrictions.eq("categoryName", "Romantic"));
countryCriteria.add(Restrictions.like("countryName", "",
MatchMode.ANYWHERE));
List movieList = movieCriteria.list();
ProjectionList에 SELECT 절을 구성할 조회 대상 attribute들을 추가시키고 as() 메소드를 이용하여 각각의 attribute에 대한
alias를 정의할 수 있다. AliasToBeanResultTransformer 클래스를 사용하여 조회 결과의 형태를 Movie 클래스로 지정해준다.
따라서 위에서 정의한 Criteria 수행 결과는 Movie 객체의 List 형태가 될 것이다.
Movie movie1 = (Movie) movieList.get(0);
movie1.getTitle();
movie1.getDirector();
Case 2. Map 형태로 전달
Relation 관계에 놓여 있는 두개의 테이블을 대상으로 Criteria를 이용한
조회 결과를 Map 형태로 전달받을 수 있다.
Criteria movieCriteria = session.createCriteria(Movie.class);
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.id().as("movieId"));
projectionList.add(Projections.property("title").as("title"));
projectionList.add(Projections.property("director").as("director"));
movieCriteria.setProjection(projectionList);
movieCriteria.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
Criteria categoryCriteria = movieCriteria.createCriteria("categories",
"category");
Criteria countryCriteria = movieCriteria.createCriteria("country",
"country");
categoryCriteria.add(Restrictions.eq("categoryName", "Romantic"));
countryCriteria.add(Restrictions.like("countryName", "",
MatchMode.ANYWHERE));
List movieList = movieCriteria.list();
위에서 생성한 Criteria의 resultTransformer를 ALIAS_TO_ENTITY_MAP으로 지정하여
Map 형태의 결과값으로 전달 받을 수 있다.
이 때 조회 결과는 Map의 List 형태이며, alias로 정의한 movieId, title, director 등이 Map의 Key 값이 된다.
따라서 다음과 같이 Map의 Key 값을 통해 다음과 같이 결과값을 알아낼 수 있다.
Map movie1 = (Map) movieList.get(0);
movie1.get("title");
movie1.get("director");
위에서 언급된 코드는
HibernateCriteriaWithDefinedResult.java
에서 확인할 수 있다.
Pagination
Criteria를 이용하여 객체 조회시 페이징 처리된 결과를 얻기 위한 방법에 대해 알아본다.
HQL을 사용한 Pagination과 마찬가지로 시작해야 하는 Row의 Number(FirstResult)와 조회 목록의 개수(MaxResult)를 정의함으로써,
페이징 처리를 할 수 있다. 사용 예는 다음과 같다.
Criteria criteria = session.createCriteria(Movie.class);
criteria.setFirstResult(1);
criteria.setMaxResults(2);
List movieList = criteria.list();
위와 같이 정의할 경우 Hibernate Configuration 파일(hibernate.cfg.xml)에 정의된
hibernate.dialect 속성에 따라 각각의 DB에 맞는 SQL을 생성한다. 이는 Pagination을 할 때 모든 데이터를 읽은 후 해당 페이지에 속한 데이터 갯수를 결과값으로 전달하는 것이 아니라
조회해야할 데이터 즉, 해당 페이지에 속한 갯수만큼의 데이터만 읽어오게 된다.
다음은 hibernate.dialect를 HSQL DB로 정의하였을 때 페이징 처리가 되어 수행된 쿼리문이다.
select limit 1 2 this_.MOVIE_ID as MOVIE1_3_0_, this_.COUNTRY_CODE as COUNTRY2_3_0_, this_.TITLE
as TITLE3_0_, this_.DIRECTOR as DIRECTOR3_0_, this_.RELEASE_DATE as RELEASE5_3_0_ from PUBLIC.MOVIE
this_
위의 코드에서 정의한 것처럼 첫번째로 조회해야 할 항목의 번호를 1, 조회 항목의 전체 개수를 2로 정의하였으므로
Hibernate에서는 HSQL DB의 특성에 맞게 'limit 1 2'가 추가된 SQL을 실행하여 페이징 처리를 수행하였다. 위의 코드는
HibernateCriteriaPagingTest.java
에서 확인할 수 있다.