spring-projects / spring-projects/spring-framework
Introduce non-null transaction callback/result option for `TransactionOperations` (`@NullMarked` / JSpecify ergonomics)
Nobody has claimed this yet.
- 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
- Is a first-class non-null transaction execution path aligned with Spring transaction API design?
- Given lambda ambiguity risk, what API shape would you prefer?
- overload with dedicated callback type,
- distinct method name,
- or another approach.
- If runtime enforcement is used, what exception semantics/message would be preferred when
nullis returned unexpectedly?
If this direction is useful, I’m happy to prepare a PR.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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