Home 15. 추가 쿼리 기능
Post
Cancel

15. 추가 쿼리 기능

01. 수정 쿼리와 삭제 쿼리

1-1. 수정 쿼리

1
2
3
Query query = em.createQuery(
"update Hotel h set h.name = :newName where h.name = :oldName"
);

JPQL의 update 쿼리를 사용하려면 트랜잭션 범위 안에서 실행해야 한다. 트랜잭션 범위 안에서 실행하지 않을 경우 exception이 발생한다.

그리고 update 쿼리를 생성할 때는 결과 타입을 받지 않는 createQuery()를 사용한다. 따라서 TypedQuery가 아닌 Query 타입을 사용한다.

1
2
3
4
5
6
7
8
9
10
//크리테리아를 이용한 수정 쿼리
CriteriaUpdate<Hotel> cu = cb.createCriteriaUpdate(Hotel.class);
Root<Hotel> h = cu.from(Hotel.class);
cu.set(h.get("name"), "베스트웨스턴 구로");
cu.where(cb.equal(h.get("name"), "베스트웨스턴 구로호텔"));

Query query = em.createQuery(cu);
query.executeUpdate();    //실제 쿼리 실행
em.getTransacion().commit();

1-2. 삭제 쿼리

1
2
3
4
//SQL에는 from 절이 있지만 JPQL은 from 절이 없다.
Query query = em.createQuery(
"delete Hotel h set h.name = :name"
);
1
2
3
4
5
6
7
//크리테리아를 사용한 삭제 쿼리
CriteriaDelete<Hotel> cd = cb.createCriteriaDelete(Hotel.class);
Root<Hotel> h = cd.from(Hotel.class);
cd.where(cb.equal(h.get("name"), "베스트웨스턴 구로호텔"));

Query query = em.createQuery(cd);
query.executeUpdate();

1-3. 수정/삭제 쿼리와 영속 컨텍스트

수정, 삭제 쿼리를 실행할 때 주의할 점은 영속 컨텍스트에 보관된 객체는 이 쿼리에 영향을 받지 않는다는 점이다.

1
2
3
4
5
6
7
8
Query query = em.createQuery(
  "update Hotel h set h.name = :newName where h.id = :id"
);
query.setParameter("newName", "베스트웨스턴 구로");
query.setParameter("id", "H100-01");
query.executeUpdate();

hotel.getName(); //베스트웨스턴 구로호텔(변경전)

위 코드를 보면 hotel.getName()시에 update 쿼리로 수정한 “베스트웨스턴 구로”가 아닌 수정전 결과 값이 나온다. 즉 update 쿼리를 이용해서 수정한 결과는 영속 컨텍스트에 반영되지 않는 것이다. 영속 컨텍스트에 보관된 엔티티에 수정 쿼리의 결과를 반영하고 싶다면, refresh()메서드를 사용해서 DB에서 데이터를 읽어와 엔티티에 반영하면 된다. (단, delete 쿼리로 삭제한 엔티티에 대해 refresh()를 실행하면 exception이 발생한다.)

02. 네이티브 쿼리

모든 DB 작업을 JPA 쿼리로 처리할 수는 없다. DBMS에 특화된 기능이 필요하면 SQL을 사용해야 한다. 이럴 때 필요한 것이 네이티브 쿼리이다.

2-1. Object 배열로 결과를 조회하는 네이티브 쿼리

1
2
3
4
5
6
7
8
9
10
11
12
13
Query query = em.createNativeQuery(
  "select id, name, grade from hotel" +
  "where grade = :grade order by id asc limit :first, :max");
query.setParameter("grade", "STAR4");
query.setParameter("first", 0);
query.setParameter("max", 1);

List<Objet[]> resultList = query.getResultList();
for(Object[] row: resultList) {
  String id = (String) row[0];
  String name = (String) row[2];
  String grade = (String) row[3];
}

2-2. 엔티티 매핑으로 결과 조회

1
2
3
4
5
6
7
8
9
10
11
12
13
Query query = em.createNativeQuery(
  "select id, name, grade, zipcode, address1, address2" +
  "from hote where grade = :grade orderby id asc", Hotel.class);
query.setParameter("grade", "STAR4");
List<Hotel> resultList = query.getResultList();
for(Hotel hotel : resultList) {
  System.out.printf("%s %s %s \n", hotel.getId(), hotel.getName(), hotel.getGrade());
}

if(resultList.size() > 0){
  Hotel hotel = resultList.get(0);
  hotel.changeAddress(new Address("12345", "서울", "구로");
}

2-3. 네임드 네이티브 쿼리 사용

xml을 이용한 설정 346~348p

03. 하이버네이트 @Subselect

@Subselect는 쿼리 결과를 @Entity로 매핑할 수 있는 유용한 기능이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
@Immutable
@Subselect("select s.id, s.name, d.hours_op as hoursOperation" +
          "from sight s, sight_detail d" +
          "where s.id = d.sight_id"
)
@Synchronize({"Sight", "sight_detail"})
public class BestSughtSummary {
  @Id
  private Long id;
  private String name;
  private String hoursOperation;

  //...
}

@Immutable, @Subselect, @Synchronize는 하이버네이트 전용 애노테이션으로 이 태그를 사용하면 테이블이 아닌 쿼리 결과를 @Entity로 매핑할 수 있다.

  • @Subselect - 조회(select) 쿼리를 값으로 갖는다. 이 쿼리의 결과를 매핑할 테이블처럼 사용한다.
  • @Immutable - 엔티티의 변경 내역을 DB에 반영하지 않고 무시한다.
  • @Synchronize - 하이버네이트는 쿼리를 실행하기 전에 지정한 테이블과 관련된 변경이 있다면 엔티티 변경 내역을 플러시 한다.
This post is licensed under CC BY 4.0 by the author.