Skip to content

Route JPAInsertClause.execute() through native SQL when values contain function templates (#1757)#1758

Merged
velo merged 3 commits into
OpenFeign:masterfrom
zio0911:fix/jpa-insert-execute-native-1757
Jun 1, 2026
Merged

Route JPAInsertClause.execute() through native SQL when values contain function templates (#1757)#1758
velo merged 3 commits into
OpenFeign:masterfrom
zio0911:fix/jpa-insert-execute-native-1757

Conversation

@zio0911
Copy link
Copy Markdown
Contributor

@zio0911 zio0911 commented May 26, 2026

Summary

JPAInsertClause.execute() and HibernateInsertClause.execute() now route through JpaNativeInsertSerializer (native SQL) when value expressions contain a TemplateExpression — typically a schema-qualified function call from SQLExpressions.function/stringFunction/numberFunction or a raw SQL fragment. Plain path/literal/parameter INSERTs continue to use the existing JPQLSerializer path so JPA semantics (cascade behaviour for paths, callbacks where applicable) are preserved.

Aligns execute() with the native routing that executeWithKey() / executeWithKeys() (#1693) already use, so callers no longer need to invoke a key-returning method purely to dodge the JPQL parser when their values contain function templates.

Fixes #1757.

Changes

  • JPAInsertClause.execute() — detect template values via a new hasTemplateValue() helper and dispatch to native path when present, falling back to JPQL otherwise.
  • HibernateInsertClause.execute() — same change applied with Session.doReturningWork(...) instead of EntityManager.unwrap.
  • JpaInsertNativeHelper — added executeUpdate(Connection, String, Object[]) for the non-key-returning native path. Class javadoc generalized.
  • Regression tests in JPAExecuteWithKeyTest and HibernateExecuteWithKeyTest:
    • execute_with_function_template_routes_through_native_sql — verifies that upper({0}) inside a value expression is preserved and the function is evaluated by the DB.
    • execute_without_template_uses_jpql_path — verifies plain INSERTs still go through JPQL.

Test plan

…n function templates (OpenFeign#1757)

Detect TemplateExpression values via a shared hasTemplateValue() helper and route execute()
to JpaNativeInsertSerializer + JDBC executeUpdate when present, falling back to the existing
JPQL path otherwise. Same change applied to HibernateInsertClause.

Adds executeUpdate(Connection, String, Object[]) to JpaInsertNativeHelper and regression
tests covering both routing branches.

Closes OpenFeign#1757
Copy link
Copy Markdown
Member

@velo velo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix and tests. I’m requesting changes due to a backwards-compatibility concern.

Gate results:

  1. Test coverage: PASS (new tests added for template and non-template execute paths in both JPA and Hibernate variants).
  2. Backwards compatibility: FAIL
  3. Security: PASS (uses prepared statements with bound parameters; no obvious injection risk introduced).

Blocking issue (backwards compatibility):

  • In querydsl-jpa JPAInsertClause.execute(), when hasTemplateValue() is true, the new implementation unconditionally unwraps org.hibernate.Session and calls doReturningWork(...).
    This hard-requires Hibernate at runtime for that code path. querydsl-jpa is used with non-Hibernate JPA providers (e.g., EclipseLink/OpenJPA), so this can fail at runtime (unwrap failure) where previously execute() stayed provider-neutral.

Please add provider-safe handling, e.g.:

  • guard unwrap with capability checks and provide a provider-neutral fallback, or
  • fail fast with a clear, documented exception only for unsupported providers and preserve previous behavior otherwise.

Once this is addressed, I’m happy to re-review.

Use the JPA-standard EntityManager.createNativeQuery instead of unwrapping
a Hibernate Session, so the native routing for INSERTs with function
templates also works under EclipseLink/OpenJPA. Addresses the backwards-
compatibility review on OpenFeign#1758.

HibernateInsertClause is intentionally unchanged — its native path
continues to use Session.doReturningWork since that class is
Hibernate-specific by design.
@zio0911
Copy link
Copy Markdown
Contributor Author

zio0911 commented Jun 1, 2026

@velo
Addressed in fd7f8e9.

Instead of guarding the unwrap, I switched JPAInsertClause.execute() to use the JPA-standard EntityManager.createNativeQuery(...) for the native path. This removes the Hibernate dependency entirely from this code path — it now works under any JPA provider (EclipseLink, OpenJPA, etc.) without capability checks or fallback logic.

HibernateInsertClause.execute() is intentionally left unchanged: that class is Hibernate-specific by design and continues to use Session.doReturningWork(...), consistent with its existing executeWithKey path.

3238 tests pass locally on -Pdev. Ready for re-review.

Copy link
Copy Markdown
Member

@velo velo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update and for addressing the provider-compatibility concern. This now includes focused regression tests for both routing paths, preserves existing behavior for non-template inserts, and checks are green.

@velo velo merged commit 7e94db3 into OpenFeign:master Jun 1, 2026
4 checks passed
@zio0911 zio0911 deleted the fix/jpa-insert-execute-native-1757 branch June 2, 2026 03:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

JPAInsertClause.execute() does not route to native SQL when values contain function templates / SQL fragments

3 participants