Hibernate Native SQL Query 예제

Hibernate Native SQL 쿼리 예제 튜토리얼에 오신 것을 환영합니다. 이전 글에서는 Hibernate Query LanguageHibernate Criteria를 살펴보았습니다. 오늘은 Hibernate Native SQL 쿼리에 대해 예제로 살펴보겠습니다.

Hibernate SQL 쿼리

하이버네이트는 SQLQuery 객체를 통해 네이티브 SQL 쿼리를 실행할 수 있는 옵션을 제공합니다. 하이버네이트 SQL 쿼리는 하이버네이트 API에서 지원되지 않는 데이터베이스 벤더별 쿼리(예: 오라클 데이터베이스의 쿼리 힌트나 CONNECT 키워드)를 실행해야 할 때 매우 편리합니다. 일반적인 시나리오에서는 하이버네이트 SQL 쿼리가 하이버네이트 연관 및 하이버네이트 1차 캐시와 관련된 혜택을 잃어버리기 때문에 권장되지 않습니다. 저는 MySQL 데이터베이스를 사용하고 HQL 예제와 동일한 테이블 및 데이터 설정을 사용할 것이므로 먼저 해당 예제를 확인하여 테이블 및 해당 모델 클래스 매핑을 이해해야 합니다.

하이버네이트 네이티브 SQL 예제

하이버네이트 네이티브 SQL 쿼리를 위해 Session.createSQLQuery(String query)를 사용하여 SQLQuery 객체를 생성하고 실행합니다. 예를 들어, Employee 테이블에서 모든 레코드를 읽고 싶다면 아래 코드를 사용할 수 있습니다.

// 준비 작업
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.getCurrentSession();

// 모든 직원 가져오기
Transaction tx = session.beginTransaction();
SQLQuery query = session.createSQLQuery("select emp_id, emp_name, emp_salary from Employee");
List rows = query.list();
for(Object[] row : rows){
	Employee emp = new Employee();
	emp.setId(Long.parseLong(row[0].toString()));
	emp.setName(row[1].toString());
	emp.setSalary(Double.parseDouble(row[2].toString()));
	System.out.println(emp);
}

위 코드를 실행하면 데이터 설정에 대한 다음 출력이 생성됩니다.

Hibernate: select emp_id, emp_name, emp_salary from Employee
Id= 1, Name= Pankaj, Salary= 100.0, {Address= null}
Id= 2, Name= David, Salary= 200.0, {Address= null}
Id= 3, Name= Lisa, Salary= 300.0, {Address= null}
Id= 4, Name= Jack, Salary= 400.0, {Address= null}

list() 메서드는 객체 배열 목록을 반환하며, 이를 명시적으로 double, long 등으로 구문 분석해야 합니다. 저희의 Employee 및 Address 클래스에는 다음과 같은 toString() 메서드 구현이 있습니다.

@Override
public String toString() {
	return "Id= " + id + ", Name= " + name + ", Salary= " + salary
			+ ", {Address= " + address + "}";
}
@Override
public String toString() {
	return "AddressLine1= " + addressLine1 + ", City=" + city
			+ ", Zipcode=" + zipcode;
}

쿼리가 Address 데이터를 반환하지 않는다는 점에 유의하세요. 반면 HQL 쿼리 "from Employee"를 사용하면 관련 테이블 데이터도 반환됩니다.

Hibernate SQL Query addScalar

Hibernate는 쿼리에서 반환된 열의 유형을 추론하기 위해 ResultSetMetadata를 사용합니다. 성능 관점에서 열의 데이터 유형을 정의하기 위해 addScalar() 메서드를 사용할 수 있습니다. 그러나 여전히 데이터는 객체 배열 형태로 얻게 됩니다.

// 모든 직원 가져오기 - addScalar 예제
query = session.createSQLQuery("select emp_id, emp_name, emp_salary from Employee")
		.addScalar("emp_id", new LongType())
		.addScalar("emp_name", new StringType())
		.addScalar("emp_salary", new DoubleType());
rows = query.list();
for(Object[] row : rows){
	Employee emp = new Employee();
	emp.setId(Long.parseLong(row[0].toString()));
	emp.setName(row[1].toString());
	emp.setSalary(Double.parseDouble(row[2].toString()));
	System.out.println(emp);
}

생성된 출력물은 동일하지만 데이터가 많을 때 약간의 성능 향상이 있습니다.

Hibernate Native SQL Multiple Tables

Employee 및 Address 테이블에서 데이터를 가져오고 싶다면 해당하는 SQL 쿼리를 작성하고 결과 집합을 구문 분석할 수 있습니다.

query = session.createSQLQuery("select e.emp_id, emp_name, emp_salary,address_line1, city, 
	zipcode from Employee e, Address a where a.emp_id=e.emp_id");
rows = query.list();
for(Object[] row : rows){
	Employee emp = new Employee();
	emp.setId(Long.parseLong(row[0].toString()));
	emp.setName(row[1].toString());
	emp.setSalary(Double.parseDouble(row[2].toString()));
	Address address = new Address();
	address.setAddressLine1(row[3].toString());
	address.setCity(row[4].toString());
	address.setZipcode(row[5].toString());
	emp.setAddress(address);
	System.out.println(emp);
}

위의 코드에 대한 출력물은 아래와 같이 생성됩니다.

Hibernate: select e.emp_id, emp_name, emp_salary,address_line1, city, zipcode from Employee e, Address a where a.emp_id=e.emp_id
Id= 1, Name= Pankaj, Salary= 100.0, {Address= AddressLine1= Albany Dr, City=San Jose, Zipcode=95129}
Id= 2, Name= David, Salary= 200.0, {Address= AddressLine1= Arques Ave, City=Santa Clara, Zipcode=95051}
Id= 3, Name= Lisa, Salary= 300.0, {Address= AddressLine1= BTM 1st Stage, City=Bangalore, Zipcode=560100}
Id= 4, Name= Jack, Salary= 400.0, {Address= AddressLine1= City Centre, City=New Delhi, Zipcode=100100}

Hibernate 네이티브 SQL 엔터티 및 조인

우리는 또한 addEntity()addJoin() 메서드를 사용하여 연결된 테이블에서 데이터를 가져올 수 있습니다. 예를 들어, 위의 데이터는 다음과 같이 검색할 수도 있습니다.

//addEntity 및 addJoin과 함께 조인 예제
query = session.createSQLQuery("select {e.*}, {a.*} from Employee e join Address a ON e.emp_id=a.emp_id")
		.addEntity("e",Employee.class)
		.addJoin("a","e.address");
rows = query.list();
for (Object[] row : rows) {
    for(Object obj : row) {
    	System.out.print(obj + "::");
    }
    System.out.println("\n");
}
//위의 조인은 배열에 Employee 및 Address 객체를 모두 반환합니다.
for (Object[] row : rows) {
	Employee e = (Employee) row[0];
	System.out.println("Employee Info::"+e);
	Address a = (Address) row[1];
	System.out.println("Address Info::"+a);
}

{[aliasname].*}는 엔터티의 모든 속성을 반환하는 데 사용됩니다. 위와 같은 조인 쿼리와 함께 addEntity()addJoin()을 사용하면 위와 같이 두 객체를 반환합니다. 위의 코드에서 생성된 출력물은 아래와 같습니다.

Hibernate: select e.emp_id as emp_id1_1_0_, e.emp_name as emp_name2_1_0_, e.emp_salary as emp_sala3_1_0_, a.emp_id as emp_id1_0_1_, a.address_line1 as address_2_0_1_, a.city as city3_0_1_, a.zipcode as zipcode4_0_1_ from Employee e join Address a ON e.emp_id=a.emp_id
Id= 1, Name= Pankaj, Salary= 100.0, {Address= AddressLine1= Albany Dr, City=San Jose, Zipcode=95129}::AddressLine1= Albany Dr, City=San Jose, Zipcode=95129::

Id= 2, Name= David, Salary= 200.0, {Address= AddressLine1= Arques Ave, City=Santa Clara, Zipcode=95051}::AddressLine1= Arques Ave, City=Santa Clara, Zipcode=95051::

Id= 3, Name= Lisa, Salary= 300.0, {Address= AddressLine1= BTM 1st Stage, City=Bangalore, Zipcode=560100}::AddressLine1= BTM 1st Stage, City=Bangalore, Zipcode=560100::

Id= 4, Name= Jack, Salary= 400.0, {Address= AddressLine1= City Centre, City=New Delhi, Zipcode=100100}::AddressLine1= City Centre, City=New Delhi, Zipcode=100100::

Employee Info::Id= 1, Name= Pankaj, Salary= 100.0, {Address= AddressLine1= Albany Dr, City=San Jose, Zipcode=95129}
Address Info::AddressLine1= Albany Dr, City=San Jose, Zipcode=95129
Employee Info::Id= 2, Name= David, Salary= 200.0, {Address= AddressLine1= Arques Ave, City=Santa Clara, Zipcode=95051}
Address Info::AddressLine1= Arques Ave, City=Santa Clara, Zipcode=95051
Employee Info::Id= 3, Name= Lisa, Salary= 300.0, {Address= AddressLine1= BTM 1st Stage, City=Bangalore, Zipcode=560100}
Address Info::AddressLine1= BTM 1st Stage, City=Bangalore, Zipcode=560100
Employee Info::Id= 4, Name= Jack, Salary= 400.0, {Address= AddressLine1= City Centre, City=New Delhi, Zipcode=100100}
Address Info::AddressLine1= City Centre, City=New Delhi, Zipcode=100100

mysql 클라이언트에서 두 쿼리를 모두 실행하고 생성된 출력이 동일한 것을 확인할 수 있습니다.

mysql> select e.emp_id as emp_id1_1_0_, e.emp_name as emp_name2_1_0_, e.emp_salary as emp_sala3_1_0_, a.emp_id as emp_id1_0_1_, a.address_line1 as address_2_0_1_, a.city as city3_0_1_, a.zipcode as zipcode4_0_1_ from Employee e join Address a ON e.emp_id=a.emp_id;
+--------------+----------------+----------------+--------------+----------------+-------------+---------------+
| emp_id1_1_0_ | emp_name2_1_0_ | emp_sala3_1_0_ | emp_id1_0_1_ | address_2_0_1_ | city3_0_1_  | zipcode4_0_1_ |
+--------------+----------------+----------------+--------------+----------------+-------------+---------------+
|            1 | Pankaj         |            100 |            1 | Albany Dr      | San Jose    | 95129         |
|            2 | David          |            200 |            2 | Arques Ave     | Santa Clara | 95051         |
|            3 | Lisa           |            300 |            3 | BTM 1st Stage  | Bangalore   | 560100        |
|            4 | Jack           |            400 |            4 | City Centre    | New Delhi   | 100100        |
+--------------+----------------+----------------+--------------+----------------+-------------+---------------+
4 rows in set (0.00 sec)

mysql> select e.emp_id, emp_name, emp_salary,address_line1, city, zipcode from Employee e, Address a where a.emp_id=e.emp_id;
+--------+----------+------------+---------------+-------------+---------+
| emp_id | emp_name | emp_salary | address_line1 | city        | zipcode |
+--------+----------+------------+---------------+-------------+---------+
|      1 | Pankaj   |        100 | Albany Dr     | San Jose    | 95129   |
|      2 | David    |        200 | Arques Ave    | Santa Clara | 95051   |
|      3 | Lisa     |        300 | BTM 1st Stage | Bangalore   | 560100  |
|      4 | Jack     |        400 | City Centre   | New Delhi   | 100100  |
+--------+----------+------------+---------------+-------------+---------+
4 rows in set (0.00 sec)

mysql> 

매개변수와 함께 하는 Hibernate 네이티브 SQL 쿼리

저희는 하이버네이트 SQL 쿼리에도 JDBC PreparedStatement와 같이 매개변수를 전달할 수 있습니다. 매개변수는 아래 예시에 표시된 것처럼 이름 뿐만 아니라 인덱스를 사용하여 설정할 수 있습니다.

query = session
		.createSQLQuery("select emp_id, emp_name, emp_salary from Employee where emp_id = ?");
List<Object[]> empData = query.setLong(0, 1L).list();
for (Object[] row : empData) {
	Employee emp = new Employee();
	emp.setId(Long.parseLong(row[0].toString()));
	emp.setName(row[1].toString());
	emp.setSalary(Double.parseDouble(row[2].toString()));
	System.out.println(emp);
}

query = session
		.createSQLQuery("select emp_id, emp_name, emp_salary from Employee where emp_id = :id");
empData = query.setLong("id", 2L).list();
for (Object[] row : empData) {
	Employee emp = new Employee();
	emp.setId(Long.parseLong(row[0].toString()));
	emp.setName(row[1].toString());
	emp.setSalary(Double.parseDouble(row[2].toString()));
	System.out.println(emp);
}

위 코드에서 생성된 출력은 다음과 같습니다:

Hibernate: select emp_id, emp_name, emp_salary from Employee where emp_id = ?
Id= 1, Name= Pankaj, Salary= 100.0, {Address= null}
Hibernate: select emp_id, emp_name, emp_salary from Employee where emp_id = ?
Id= 2, Name= David, Salary= 200.0, {Address= null}

하이버네이트 SQL 쿼리에 대한 간략한 소개였습니다. 데이터베이스별 쿼리를 실행하고자하지 않는 한 사용을 피해야합니다.

Source:
https://www.digitalocean.com/community/tutorials/hibernate-native-sql-query-example