Agent Skills: Hibernate 5 to 6 Upgrade Skill

Migrate Hibernate 5 to Hibernate 6 with Spring Boot 3. Use when fixing HQL/JPQL query parsing issues, removing deprecated Criteria API, updating ID generation strategies, or diagnosing N+1 query behavior changes. Covers breaking changes, type mappings, and performance monitoring.

UncategorizedID: benchflow-ai/skillsbench/hibernate-upgrade

Install this agent skill to your local

pnpm dlx add-skill https://github.com/benchflow-ai/skillsbench/tree/HEAD/tasks/spring-boot-jakarta-migration/environment/skills/hibernate-upgrade

Skill Files

Browse the full folder contents for hibernate-upgrade.

Download Skill

Loading file tree…

tasks/spring-boot-jakarta-migration/environment/skills/hibernate-upgrade/SKILL.md

Skill Metadata

Name
hibernate-upgrade
Description
Migrate Hibernate 5 to Hibernate 6 with Spring Boot 3. Use when fixing HQL/JPQL query parsing issues, removing deprecated Criteria API, updating ID generation strategies, or diagnosing N+1 query behavior changes. Covers breaking changes, type mappings, and performance monitoring.

Hibernate 5 to 6 Upgrade Skill

Overview

Spring Boot 3 uses Hibernate 6, which includes significant changes from Hibernate 5. This skill covers the key migration considerations.

Key Changes

1. Package Namespace

Hibernate 6 uses Jakarta Persistence:

// Before (Hibernate 5)
import javax.persistence.*;
import org.hibernate.annotations.*;

// After (Hibernate 6)
import jakarta.persistence.*;
import org.hibernate.annotations.*;

2. ID Generation Strategies

Hibernate 6 changed the default ID generation strategy:

// Recommended approach for cross-database compatibility
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// Or using sequences (preferred for some databases)
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
@SequenceGenerator(name = "user_seq", sequenceName = "user_sequence", allocationSize = 1)
private Long id;

3. Dialect Configuration

Hibernate 6 can auto-detect dialects in most cases:

# Before (often required in Hibernate 5)
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

# After (Hibernate 6) - often can be removed
# Hibernate auto-detects based on JDBC URL
# Only specify if you need specific behavior

If you must specify:

# Hibernate 6 dialects (some renamed)
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

4. Query Changes

JPQL Changes

Some JPQL syntax has stricter validation:

// Be explicit about entity aliases
@Query("SELECT u FROM User u WHERE u.active = true")
List<User> findActiveUsers();

// Avoid implicit joins - be explicit
@Query("SELECT u FROM User u JOIN u.roles r WHERE r.name = :roleName")
List<User> findByRoleName(@Param("roleName") String roleName);

Native Queries

Native query result mapping changed:

// Before (Hibernate 5)
@Query(value = "SELECT * FROM users WHERE active = 1", nativeQuery = true)
List<User> findActiveUsersNative();

// After (Hibernate 6) - works the same, but be careful with projections
@Query(value = "SELECT * FROM users WHERE active = true", nativeQuery = true)
List<User> findActiveUsersNative();

5. Type Mappings

Some type mappings changed:

// Enum mapping - explicit is better
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Role role;

// Date/Time - use java.time classes
@Column(name = "created_at")
private LocalDateTime createdAt;  // Preferred over java.util.Date

6. Fetch Strategies

Default fetch behavior remains similar, but be explicit:

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Department department;

@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<Order> orders;

Deprecation Removals

Removed in Hibernate 6

  1. @Type annotation - replaced with @JdbcTypeCode or custom type definitions
  2. Legacy ID generators - use standard JPA generation
  3. @TypeDef - removed, use @Type with explicit class

Example Migration

// Before (Hibernate 5 with custom type)
@TypeDef(name = "json", typeClass = JsonType.class)
@Type(type = "json")
private JsonNode metadata;

// After (Hibernate 6)
@JdbcTypeCode(SqlTypes.JSON)
private JsonNode metadata;

Configuration Properties

Some Hibernate properties changed:

# Common properties that remain the same
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true

# Properties that may need review
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true

# New in Hibernate 6
spring.jpa.properties.hibernate.timezone.default_storage=NORMALIZE

Testing Considerations

  1. Entity validation - Hibernate 6 is stricter about entity mappings
  2. Query parsing - Some queries that worked in H5 may fail in H6
  3. Performance - Test performance as query execution plans may differ

Troubleshooting

Common Errors

  1. "Unknown entity" - Ensure entity scanning is configured correctly
  2. "Could not determine type" - Check type mappings and annotations
  3. "Query syntax error" - Review JPQL for stricter H6 parsing rules

Critical Breaking Changes

1. HQL/JPQL Query Parsing is Stricter

// BEFORE (Hibernate 5) - this worked but was non-standard
@Query("update from User u set u.active = false where u.id = :id")

// AFTER (Hibernate 6) - remove the optional "from" keyword
@Query("update User u set u.active = false where u.id = :id")

2. Distinct is No Longer Needed for Collections

// BEFORE (Hibernate 5) - needed distinct to avoid duplicates
@Query("select distinct u from User u join fetch u.roles")

// AFTER (Hibernate 6) - duplicates are automatically filtered
@Query("select u from User u join fetch u.roles")

3. Legacy Criteria API Removed

The deprecated org.hibernate.Criteria API is completely removed. You must use JPA Criteria API:

// BEFORE (Hibernate 5 - old Criteria)
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.eq("active", true));
List<User> users = criteria.list();

// AFTER (Hibernate 6 - JPA Criteria)
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> cq = cb.createQuery(User.class);
Root<User> root = cq.from(User.class);
cq.where(cb.equal(root.get("active"), true));
List<User> users = entityManager.createQuery(cq).getResultList();

4. N+1 Query Behavior Changes

Hibernate 6 may generate different SQL for lazy-loaded collections. Monitor your queries:

// Add this to application.properties to monitor queries
spring.jpa.properties.hibernate.generate_statistics=true
logging.level.org.hibernate.stat=debug
logging.level.org.hibernate.SQL=debug

Migration Commands

Check for Hibernate 5 Patterns

# Check for old Criteria API usage
grep -r "session.createCriteria\|org.hibernate.Criteria" --include="*.java" .

# Check for deprecated @Type usage
grep -r "@Type(type\s*=" --include="*.java" .

# Check for @TypeDef
grep -r "@TypeDef" --include="*.java" .

# Check for "update from" pattern in queries
grep -r "update from" --include="*.java" .

Fix Common Issues

# Remove "from" keyword in update queries
find . -name "*.java" -type f -exec sed -i 's/update from /update /g' {} +

Performance Monitoring

After migration, monitor for performance regressions:

# Enable Hibernate statistics
spring.jpa.properties.hibernate.generate_statistics=true

# Log slow queries (Hibernate 6.2+)
spring.jpa.properties.hibernate.session.events.log.LOG_QUERIES_SLOWER_THAN_MS=100

Sources