Hibernate 7 변경점 — Spring Boot 4.0과 함께 달라진 것들
Hibernate 버전이 바뀌면 그냥 의존성만 올리면 되는 걸까요? 아니면 코드도 바꿔야 할까요?
Spring Boot 4.0이 기본 ORM으로 Hibernate 7.1을 채택하면서, 단순한 버전 업그레이드 이상의 변화가 생겼습니다. Jakarta Persistence 3.2 기반 전환, 타입 시스템 정리, StatelessSession 개선 등 실무에서 바로 영향을 받는 변경점이 꽤 많습니다.
이 글에서는 Hibernate 6.x에서 7.x로 넘어가면서 달라진 것들을 정리하고, Spring Data 2025.1과의 통합까지 살펴봅니다.
왜 Hibernate 7이 중요한가
Spring Boot 4.0의 기본 ORM이 Hibernate 7.1입니다. Boot 4.0으로 업그레이드하면 자연스럽게 Hibernate 7도 함께 따라옵니다.
- Jakarta Persistence 3.2 완전 지원 — 표준 스펙이 달라졌습니다
- Java 17 최소 요구 — Java 11, 16은 더 이상 지원하지 않습니다
- ** 레거시 API 대거 정리** — deprecated였던 것들이 실제로 제거되었습니다
단순히 "새 버전이 나왔다"가 아니라, 기존 코드가 컴파일되지 않을 수 있는 수준의 변경입니다. 마이그레이션 가이드를 반드시 확인해야 합니다.
Jakarta Persistence 3.2 변경사항
Hibernate 7의 가장 큰 기반 변경입니다. Jakarta Persistence 3.2는 기존 스펙에서 몇 가지 중요한 추가와 변경이 있습니다.
패키지 전환 최종 정착
Boot 3.x에서 이미 javax.persistence → jakarta.persistence 전환이 이루어졌지만, 3.2에서는 새로운 API가 추가되면서 이 전환이 완전히 정착됩니다.
Criteria API 개선
// Hibernate 7 + Jakarta Persistence 3.2
// union, intersect, except 지원
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<String> activeQuery = cb.createQuery(String.class);
Root<Member> m1 = activeQuery.from(Member.class);
activeQuery.select(m1.get("name"))
.where(cb.equal(m1.get("status"), "ACTIVE"));
CriteriaQuery<String> vipQuery = cb.createQuery(String.class);
Root<Member> m2 = vipQuery.from(Member.class);
vipQuery.select(m2.get("name"))
.where(cb.equal(m2.get("grade"), "VIP"));
// 3.2에서 추가된 union 연산
CriteriaQuery<String> unionQuery = cb.union(activeQuery, vipQuery);
List<String> names = em.createQuery(unionQuery).getResultList();
Criteria API에서 union을 지원하게 된 건 꽤 의미 있는 변화입니다. 이전에는 네이티브 쿼리를 써야 했던 부분을 타입 안전하게 처리할 수 있게 되었습니다.
StatelessSession 개선
StatelessSession은 영속성 컨텍스트 없이 동작하는 세션입니다. 1차 캐시도 없고, dirty checking도 없어서 대량 데이터 처리에 적합합니다.
Hibernate 6.x에서는 단순 CRUD만 가능하고 페치 조인이나 엔티티 그래프를 사용할 수 없었습니다. Hibernate 7에서는 다음과 같이 달라졌습니다.
// Hibernate 7 — StatelessSession 대폭 개선
StatelessSession session = sessionFactory.openStatelessSession();
Transaction tx = session.beginTransaction();
// 페치 프로파일 지원 추가
session.enableFetchProfile("memberWithTeam");
// HQL에서 엔티티 그래프 힌트 사용 가능
List<Member> members = session.createQuery(
"select m from Member m where m.status = :status", Member.class)
.setParameter("status", "ACTIVE")
.getResultList();
// upsert 지원 — INSERT or UPDATE를 한 번에
session.upsert(new Member("user01", "홍길동", "ACTIVE"));
tx.commit();
session.close();
주요 개선 사항을 정리하면 다음과 같습니다.
- ** 페치 프로파일 지원** — StatelessSession에서도 연관 엔티티를 효율적으로 로딩
- upsert 지원 — INSERT or UPDATE를 데이터베이스 레벨에서 처리
- HQL/JPQL 힌트 지원 — 쿼리 힌트를 통한 세밀한 제어 가능
배치 처리할 때 EntityManager 대신 StatelessSession을 쓰면 메모리를 아낄 수 있다는 건 이전부터 알려져 있었습니다. Hibernate 7에서는 그 StatelessSession의 활용 범위가 넓어졌다는 게 핵심입니다.
새로운 매핑 어노테이션과 변경사항
@SoftDelete — 논리 삭제 지원
이전에는 @Where 어노테이션이나 직접 구현해야 했던 논리 삭제를 표준 방식으로 지원합니다.
@Entity
@SoftDelete(columnName = "deleted") // 논리 삭제 컬럼 지정
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// deleted 컬럼은 Hibernate가 자동 관리
// em.remove() 호출 시 DELETE 대신 UPDATE deleted = true 실행
}
// 사용 코드 — 기존과 동일하게 사용하면 됨
em.remove(member); // 실제로는 UPDATE member SET deleted = true WHERE id = ?
// 조회 시 자동으로 deleted = false 조건 추가
List<Member> members = em.createQuery(
"select m from Member m", Member.class) // WHERE deleted = false 자동 추가
.getResultList();
@JdbcTypeCode — JSON 네이티브 지원
@Entity
public class Order {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// JSON 타입 네이티브 지원 (별도 라이브러리 불필요)
@JdbcTypeCode(SqlTypes.JSON)
private Map<String, Object> metadata;
}
@Where → @SQLRestriction 전환
// Hibernate 6.x (deprecated)
@Where(clause = "status = 'ACTIVE'")
@OneToMany(mappedBy = "team")
private List<Member> activeMembers;
// Hibernate 7 — @SQLRestriction으로 대체
@SQLRestriction("status = 'ACTIVE'")
@OneToMany(mappedBy = "team")
private List<Member> activeMembers;
타입 시스템 변경
Hibernate 7에서 가장 많은 영향을 미치는 변경 중 하나입니다. 레거시 Type 시스템이 정리되고, 새로운 방식으로 대체됩니다.
제거된 것들
// 더 이상 사용 불가 — 컴파일 에러 발생
import org.hibernate.type.StringType; // 제거됨
import org.hibernate.type.IntegerType; // 제거됨
import org.hibernate.type.LongType; // 제거됨
// Hibernate 6.x 이전 방식 (더 이상 동작하지 않음)
query.setParameter("name", "홍길동", StringType.INSTANCE); // 컴파일 에러
새로운 방식
// Hibernate 7 — 표준 타입 시스템 사용
// 대부분의 경우 타입 힌트가 불필요해짐
query.setParameter("name", "홍길동"); // 자동 타입 추론
// 커스텀 타입이 필요한 경우 — @JavaType, @JdbcType 사용
@Entity
public class Product {
@JavaType(MoneyJavaType.class)
@JdbcType(BigDecimalJdbcType.class)
private Money price; // 커스텀 타입 매핑
}
기존에
StringType.INSTANCE같은 Hibernate 전용 타입을 직접 참조하던 코드가 있다면, 마이그레이션 시 반드시 수정해야 합니다. 검색해보면 의외로 많이 나올 수 있습니다.
HQL/JPQL 개선
새로운 함수들
// Hibernate 7 HQL 개선 사항
// 1. 배열 함수 지원
List<Member> result = em.createQuery("""
select m from Member m
where array_contains(m.roles, 'ADMIN')
""", Member.class)
.getResultList();
// 2. window 함수 지원 확대
List<Object[]> ranked = em.createQuery("""
select m.name, m.score,
rank() over (order by m.score desc) as ranking
from Member m
""", Object[].class)
.getResultList();
// 3. 서브쿼리 개선 — FROM 절 서브쿼리
List<Object[]> stats = em.createQuery("""
select sub.teamName, sub.avgScore
from (
select m.team.name as teamName, avg(m.score) as avgScore
from Member m
group by m.team.name
) sub
where sub.avgScore > 80
""", Object[].class)
.getResultList();
HQL에서 FROM 절 서브쿼리가 가능해진 건 실무에서 꽤 쓸모 있습니다. 이전에는 네이티브 쿼리로 빠져야 했던 복잡한 통계 쿼리를 HQL로 작성할 수 있게 되었습니다.
정렬 관련 API 변경
Hibernate 전용이었던 org.hibernate.query.SortOrder가 deprecated되고, jakarta.persistence.criteria.Order 표준 API를 사용하도록 변경되었습니다.
성능 개선
기본 배치 사이즈 변경
# Hibernate 6.x — 기본값 없음 (설정 안 하면 배치 비활성화)
spring:
jpa:
properties:
hibernate:
default_batch_fetch_size: 100 # 직접 설정 필요
# Hibernate 7 — 기본 배치 사이즈가 적용됨
# 별도 설정 없이도 합리적인 배치 페칭 동작
쿼리 플랜 캐시 & 2차 캐시
- ** 쿼리 플랜 캐시** — 동일한 JPQL/HQL 반복 실행 시 파싱 비용이 줄었습니다. 캐시 크기 기본값도 증가했습니다.
- 2차 캐시 — 컬렉션 캐시의 동작이 개선되어
@Cache가 적용된@OneToMany컬렉션의 캐시 무효화가 더 정확해졌습니다.
Spring Data 2025.1 통합
Spring Boot 4.0은 Spring Data 2025.1을 사용하며, Hibernate 7과 완전히 호환됩니다.
새로운 쿼리 메서드 키워드
public interface MemberRepository extends JpaRepository<Member, Long> {
// 기존에도 있던 것들
List<Member> findByName(String name);
// Spring Data 2025.1에서 개선된 부분
// Scroll API — 대량 데이터를 커서 기반으로 조회
Window<Member> findFirst10ByTeamId(Long teamId, ScrollPosition position);
// 반환 타입으로 Stream 사용 시 성능 최적화
@QueryHints(@QueryHint(name = HINT_FETCH_SIZE, value = "50"))
Stream<Member> findAllByStatus(String status);
}
개선된 Auditing
Spring Data 2025.1에서는 @CreatedDate, @LastModifiedDate 등 Auditing 기능이 StatelessSession 환경에서도 정상 동작합니다. 배치 처리에서 Auditing이 누락되던 문제가 해결되었습니다.
Repository 프래그먼트 개선
커스텀 Repository 구현체에서 EntityManager 주입 방식이 개선되었고, Hibernate 7의 Criteria API를 바로 활용할 수 있습니다.
// 구현체 — Hibernate 7의 개선된 Criteria API 활용
class MemberRepositoryCustomImpl implements MemberRepositoryCustom {
private final EntityManager em;
MemberRepositoryCustomImpl(EntityManager em) {
this.em = em;
}
@Override
public List<Member> searchByCondition(MemberSearchCondition condition) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Member> cq = cb.createQuery(Member.class);
Root<Member> root = cq.from(Member.class);
List<Predicate> predicates = new ArrayList<>();
if (condition.getName() != null) {
predicates.add(cb.like(root.get("name"), "%" + condition.getName() + "%"));
}
cq.where(predicates.toArray(new Predicate[0]));
return em.createQuery(cq).getResultList();
}
}
마이그레이션 체크리스트: 6.x → 7.x
실제 마이그레이션할 때 확인해야 할 항목을 정리합니다.
1단계: 컴파일 에러 해결
StringType.INSTANCE등 레거시 타입 참조 → 제거 (타입 힌트 불필요)@Where→@SQLRestriction으로 교체SortOrder→jakarta.persistence.criteria.Order사용SessionFactory.openSession()시그니처 변경 확인
2단계: 설정 변경
spring:
jpa:
properties:
hibernate:
# Hibernate 7에서 변경된 설정
# hibernate.hbm2ddl.auto → 동작은 동일하나 일부 DDL 생성 방식 변경
hbm2ddl:
auto: validate # 운영에서는 validate 권장
# 새로운 설정 옵션
query:
plan_cache_max_size: 2048 # 쿼리 플랜 캐시 크기 (기본값 증가)
3단계: 테스트 실행 및 쿼리 확인
show_sql=true로 Hibernate 7이 생성하는 SQL을 확인합니다. 기존과 달라진 부분이 있을 수 있습니다.- 기존 Repository 메서드가 정상 동작하는지
@DataJpaTest로 검증합니다. - StatelessSession의 새 기능(upsert 등)은 커스텀 Repository에서 별도 테스트합니다.
주의사항 정리
| 항목 | Hibernate 6.x | Hibernate 7 |
|---|---|---|
| 최소 Java | 11 | 17 |
| Jakarta Persistence | 3.1 | 3.2 |
@Where | deprecated | 제거 → @SQLRestriction |
StringType.INSTANCE | deprecated | 제거 |
| StatelessSession | 기능 제한적 | 페치 프로파일, upsert 지원 |
| FROM 절 서브쿼리 | 미지원 | HQL에서 지원 |
@SoftDelete | 미지원 | 기본 제공 |
정리
Hibernate 7로의 마이그레이션은 단순한 버전 번호 변경이 아닙니다. 실제로 코드 수정이 필요한 부분이 있고, 특히 레거시 타입 시스템을 직접 참조하던 코드는 반드시 수정해야 합니다.
반면 StatelessSession 개선, @SoftDelete, HQL의 FROM 절 서브쿼리 같은 새 기능은 실무에서 바로 활용할 수 있는 것들입니다. Spring Boot 4.0으로 업그레이드를 계획하고 있다면, 마이그레이션 가이드를 미리 살펴보고 영향 범위를 파악해두는 것을 권합니다.
핵심만 기억한다면 이렇습니다. "Jakarta Persistence 3.2 기반 전환, 레거시 Type 제거, StatelessSession 강화." 이 세 가지가 Hibernate 7의 큰 줄기입니다.