What changed in Hibernate 6
With Hibernate 5 many teams mapped a TINYINT column to Integer/int, and SMALLINT to Integer as well. Drivers often widened values quietly, schema validation was looser, and production kept running for years.
Hibernate 6 made the Java type ↔ JDBC type pair strict via JavaType / JdbcType / SqlTypes. Canonical defaults are: byte/Byte → TINYINT, short/Short → SMALLINT, int/Integer → INTEGER. If the database column is narrower than the entity field, ddl-auto=validate fails with wrong column type.
Example 1: TINYINT is no longer “almost Integer”
Legacy mapping:
// Hibernate 5 — often worked
@Column(name = "status_code")
private Integer statusCode; // TINYINT column
After Spring Boot 3 / Hibernate 6:
Schema-validation: wrong column type encountered;
found [tinyint (Types#TINYINT)], but expecting [integer (Types#INTEGER)]
Narrow the Java type to match DDL:
@Column(name = "status_code")
private Byte statusCode; // or byte when null is impossible
If the domain API must stay on Integer, keep the field and declare the JDBC type explicitly:
@JdbcTypeCode(SqlTypes.TINYINT)
@Column(name = "status_code")
private Integer statusCode;
// or
@JdbcType(TinyIntJdbcType.class)
private Integer statusCode;
Example 2: SMALLINT → Short
// Before
private Integer countryId; // SMALLINT in PostgreSQL/MySQL
// Better
private Short countryId;
// Or explicit override
@JdbcTypeCode(SqlTypes.SMALLINT)
private Integer countryId;
Type narrowing is not pedantry — it prevents silent truncation and keeps Flyway/Liquibase DDL aligned with entities.
Example 3: enum default shift
From Hibernate 6.1+, ordinal enums default to SMALLINT instead of TINYINT (enums can exceed 255 values). On MySQL/MariaDB/H2, validate often complains:
public enum OrderStatus { NEW, PAID, SHIPPED, CANCELLED }
@Enumerated(EnumType.ORDINAL)
private OrderStatus status;
Migration options:
- widen the column to
SMALLINT; - pin the old JDBC type with
@JdbcTypeCode(SqlTypes.TINYINT); - prefer
@Enumerated(EnumType.STRING)for readable evolution.
@Enumerated(EnumType.ORDINAL)
@JdbcTypeCode(SqlTypes.TINYINT)
private OrderStatus status;
@Enumerated(EnumType.STRING)
@Column(length = 32)
private OrderStatus status;
Example 4: boolean — BOOLEAN, BIT, or TINYINT
PostgreSQL maps Boolean to BOOLEAN. MySQL historically used BIT(1) or TINYINT(1). After an upgrade the dialect may expect a different type code than your legacy schema.
@JdbcTypeCode(SqlTypes.TINYINT)
private Boolean active;
@JdbcTypeCode(SqlTypes.BIT)
private Boolean active;
Same rule: either annotate to match the existing column, or migrate DDL — do not guess both at once.
Example 5: UUID and java.time
@JdbcTypeCode(SqlTypes.UUID)
private UUID id;
private Instant createdAt;
private OffsetDateTime updatedAt;
java.util.Date still works, but new code should narrow to Instant/LocalDateTime/OffsetDateTime to avoid timezone ambiguity. For PostgreSQL uuid columns prefer real UUID types over VARCHAR(36).
Example 6: AttributeConverter instead of a magic Integer
@Converter(autoApply = true)
public class PriorityConverter implements AttributeConverter<Priority, Byte> {
public Byte convertToDatabaseColumn(Priority p) {
return p == null ? null : p.getCode(); // 1..5
}
public Priority convertToEntityAttribute(Byte code) {
return Priority.fromCode(code);
}
}
private Priority priority; // TINYINT column, domain value object/enum
You keep the JDBC type honest and lift the domain model at the same time.
Fast diagnosis
- Run
spring.jpa.hibernate.ddl-auto=validateon staging before production. - Diff Flyway DDL vs entities for
TINYINT/SMALLINT/INT/BIGINT. - Read Hibernate 6.x migration guides (enums, UUID, JSON, arrays).
- Prefer
@JdbcTypeCode/ Java-type change / column migration over heavycolumnDefinitionhacks.
Migration checklist
TINYINT→Byte/byte(or@JdbcTypeCode(SqlTypes.TINYINT)on Integer).SMALLINT→Short/short.- Ordinal enums → verify
SMALLINTvs legacyTINYINT. Boolean→ matchBIT/BOOLEAN/TINYINTfor your database.- Time →
Instant/OffsetDateTime; UUID →UUID+SqlTypes.UUID.
Takeaway
Hibernate 6 type narrowing is about an honest contract between column and entity field. int used to hide TINYINT; now you need the precise Java type (byte/short), an explicit @JdbcTypeCode, or a DDL migration. Budget that audit into every Spring Boot 2→3 upgrade — one day of column review saves a week of validate failures across environments.
