Auto-convert @Enumerated values in native JPA INSERT paths (#1883) - #1884
Open
zio0911 wants to merge 1 commit into
Open
Auto-convert @Enumerated values in native JPA INSERT paths (#1883)#1884zio0911 wants to merge 1 commit into
zio0911 wants to merge 1 commit into
Conversation
…#1883) Native INSERT paths (executeWithKey, executeWithKeys, multi-row execute) were passing enum values straight to PreparedStatement.setObject(...), leaving conversion up to the JDBC driver — MySQL usually stored .name() by accident, but other drivers fail or store the wrong representation. The native path now inspects the target column's @Enumerated annotation and converts enum values accordingly before binding: - @Enumerated(EnumType.STRING) -> bind enum.name() - @Enumerated(EnumType.ORDINAL) -> bind enum.ordinal() - Enum field with no @Enumerated -> bind enum.ordinal() (JPA default) - @convert(converter=...) on an enum field -> IllegalStateException with an actionable message; custom AttributeConverters cannot be honored on a native path that bypasses JPA. Conversion is applied uniformly to both set()-style and columns()/values() call shapes, and to every row of a multi-row INSERT accumulated via addRow(). The JPQL path (single-row execute() without templates, UPDATE, DELETE, SELECT) is unchanged — Hibernate already handles enum conversion there. Adds test coverage in JPAExecuteWithKeyTest and HibernateExecuteWithKeyTest for STRING/ORDINAL/default/@convert cases, both call shapes, and the multi-row addRow() path. GeneratedKeyEntity gains nullable enum columns so the additions are backwards-compatible with existing tests, and a top-level GeneratedKeyStatusCodeConverter is added so EclipseLink can discover the converter during persistence-unit predeployment.
zio0911
force-pushed
the
feature/jpa-insert-enum-auto-convert-1883
branch
from
August 11, 2026 01:20
e89d73e to
d98ccc7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #1883.
JPAInsertClause/HibernateInsertClauseroute through a native SQL path forexecuteWithKey(...),executeWithKeys(...), and multi-rowexecute()(accumulated viaaddRow()). On that path, enum values passed toset(EnumPath<E>, E)orvalues(E)were handed toPreparedStatement.setObject(...)as-is, so how they got stored depended entirely on the JDBC driver — MySQL usually stored.name()by accident, but H2 / HSQLDB / other drivers could fail or store the wrong representation.The native path now inspects the target column's
@Enumeratedannotation and converts enum values before binding.Behavioral changes
@Enumerated(EnumType.STRING)→ bindenum.name()@Enumerated(EnumType.ORDINAL)→ bindenum.ordinal()@Enumerated→ bindenum.ordinal()(JPA specification default)@Convert(converter = ...)on an enum field →IllegalStateExceptionwith a message pointing the caller to convert the value themselves at the call site (customAttributeConverterinstances cannot be honored on a native path that bypasses JPA)Conversion is applied uniformly to both
set(path, value)andcolumns(...).values(value)call shapes, and to every row of a multi-row INSERT accumulated viaaddRow(). The JPQL path (single-rowexecute()without templates, UPDATE, DELETE, SELECT) is unchanged — Hibernate already handles enum conversion there.Call site before / after
Before — cast tricks and manual conversion at every call site:
After — natural, type-safe call:
Scope
In scope
JPAInsertClause/HibernateInsertClauseon the native SQL execution path:executeWithKey(...)/executeWithKeys(...)execute()when it routes to the native path (multi-row viaaddRow(), and single-row when the JPQL path is bypassed for template-value INSERTs)set(path, value)andcolumns(...).values(value)call shapesaddRow()Out of scope
@Convert(converter = ...)on enum fields — a clear runtime error steers the caller to convert the value at the call sitequerydsl-sql(non-JPA) — separate module, separate binding pipelineDesign notes
Path.getAnnotatedElement()for@Enumeratedand@Convert, using the same reflection surfaceJpaNativeInsertSerializeralready uses to resolve@Columnnames. No new dependency on Hibernate-specific APIs.effectiveValues(...)and just before serialization / binding, so all four executors share a single implementation.Enum, and columns whose target does not expose an annotated element are returned unchanged — no regression on existing paths.Test plan
New tests in
JPAExecuteWithKeyTestandHibernateExecuteWithKeyTest:@Enumerated(EnumType.STRING)— stored asname()@Enumerated(EnumType.ORDINAL)— stored asordinal()(single row, multi-row viaaddRow(), andcolumns()/values()style)@Enumerated— stored asordinal()(JPA default)@Convert(converter = ...)on an enum field —IllegalStateExceptionwith an actionable message@Convertworkaround (converter output supplied at the call site) — continues to workPriority) reinforces that STRING/ORDINAL branches are generic across enum types, not specific to theStatusenum used in the primary testsRegression:
./mvnw -pl querydsl-libraries/querydsl-jpa -Pno-databases test— 437 tests pass, 0 failures.