Skillsbench hibernate-upgrade
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.
git clone https://github.com/benchflow-ai/skillsbench
T=$(mktemp -d) && git clone --depth=1 https://github.com/benchflow-ai/skillsbench "$T" && mkdir -p ~/.claude/skills && cp -r "$T/tasks/spring-boot-jakarta-migration/environment/skills/hibernate-upgrade" ~/.claude/skills/benchflow-ai-skillsbench-hibernate-upgrade && rm -rf "$T"
tasks/spring-boot-jakarta-migration/environment/skills/hibernate-upgrade/SKILL.mdHibernate 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
annotation - replaced with@Type
or custom type definitions@JdbcTypeCode- Legacy ID generators - use standard JPA generation
- removed, use@TypeDef
with explicit class@Type
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
- Entity validation - Hibernate 6 is stricter about entity mappings
- Query parsing - Some queries that worked in H5 may fail in H6
- Performance - Test performance as query execution plans may differ
Troubleshooting
Common Errors
- "Unknown entity" - Ensure entity scanning is configured correctly
- "Could not determine type" - Check type mappings and annotations
- "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