spring-projects / spring-projects/spring-framework

Introduce non-null transaction callback/result option for `TransactionOperations` (`@NullMarked` / JSpecify ergonomics)

Open
#36,655 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: data status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

Note: AI-assisted wording/structure; problem statement, proposal are my own.

Summary

I’d like to discuss improving null-safety ergonomics for programmatic transactions in spring-tx.

TransactionOperations#execute(TransactionCallback<T>) is correctly @Nullable by contract, since callbacks may return null. In @NullMarked / JSpecify codebases, this propagates nullable types into many call sites, including flows where null is not a valid business outcome.

Context / real-world problem

While refactoring production service code to @NullMarked, we repeatedly ended up with patterns like:

var result = Objects.requireNonNull(
    transactionTemplate.execute(status -> repository.merge(entity)),
    "Transactional callback returned null; this should never happen"
);

This is valid, but repetitive. We introduced an internal wrapper (TransactionalExecutor) + dedicated non-null callback type to keep call sites explicit and reduce repeated null guards.

Proposal direction

I’d like to discuss whether Spring should offer an official non-null path for this common case.

One possible shape:

<T> T execute(NonNullTransactionCallback<T> action) throws TransactionException;

Another possible shape is a distinct method name (for example executeRequired(...)) that enforces non-null results.

Naming in this issue (execute, executeRequired, NonNullTransactionCallback) is illustrative only and fully open for discussion.

Important API caveat (with minimal repro)

If a non-null callback variant is added as an overload next to existing execute(TransactionCallback<T>), lambda calls can become ambiguous because both callback types are SAM interfaces with the same shape.

Minimal example:

interface Test {
    void test(TestCallback cb);
    void test(TestCallback2 cb);
}

interface TestCallback { void test(); }
interface TestCallback2 { void test(); }

new Implementation().test(() -> System.out.println("Test")); // ambiguous

Then callers must cast:

new Implementation().test((TestCallback) () -> System.out.println("Test"));

I’m not sure this would be acceptable ergonomically for Spring users, so I wanted to raise this explicitly in the design discussion.

Why framework-level support could still help
  • Common framework-driven pain point in strict nullness code.
  • Reduces repetitive requireNonNull(...) boilerplate.
  • Makes intent explicit at call sites.
  • Encourages a consistent idiom across projects.
Questions for maintainers
  1. Is a first-class non-null transaction execution path aligned with Spring transaction API design?
  2. Given lambda ambiguity risk, what API shape would you prefer?
    • overload with dedicated callback type,
    • distinct method name,
    • or another approach.
  3. If runtime enforcement is used, what exception semantics/message would be preferred when null is returned unexpectedly?

If this direction is useful, I’m happy to prepare a PR.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with spring-tx's TransactionOperations#execute(TransactionCallback) and the TransactionCallback contract, then reproduce the lambda ambiguity shown in the issue. Review the existing transaction API design and nullness annotations before comparing the proposed API shapes. Done requires an agreed API, null-result semantics, and tests covering callback use and compatibility.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend, backend-api-design
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.