Skip to content

Safe, limited support for out-of-transaction Hibernate/JDBC usage #53931

@yrodiere

Description

@yrodiere

Description

#51244 will disallow usage of Hibernate outside of transactions by default, for safety reasons and to guide users towards best practices.

At the same time, if there are advanced use cases that might justify not using a transaction (for performance, to avoid two DB round-trips) and that are safe, we should make sure to provide a way to selectively disable our safe default. Otherwise, people with a single endpoint where no transaction is needed might end up disabling the transaction requirement across their whole application.

The only use case I can imagine at the moment is any use of Hibernate (or raw JDBC) that implies a single SQL statement:

  • If you execute a single statement, the DB should make it ACID; in particular the statement will "see" the data in a consistent, isolated state, and, in case of updates, should be atomic.
  • If you execute two statements, each will have these properties, but another transaction could happen in-between, so consistency/isolation is an issue (first statement may not see the same data as the second statement) and, in case of updates, so is atomicity (the first statement may succeed while the second one fails).

Note this excludes things like StatelessSession#insertMultiple, which unfortunately relies on multiple statements in a single JDBC batch.
It does however likely cover a great many implementations of GET rest endpoints, assuming they correctly fetch all data in a single query (e.g. using fetch join) and use appropriate fetch strategies for to-many associations (or don't need to fetch any to-many).

Implementation ideas

Could we introduce some way for the developer to declare "this operation is expected to only run a single statement in DB, so feel free to skip the actual transaction if necessary"?

E.g.:

// Returns entity types directly, assuming the use of jackson-datatype-hibernate
@Transactional
public class MyRestResource {
    @GET
    @Atomic // NEW, name TBD
    public Optional<MyEntity> get(Long id) {
        return MyEntity.repo().find(id); // Single select statement
    }
}

Then we could:

  1. Use the datasource in autocommit mode (no transaction)
  2. Handle the @Transactional annotation as a check that:
    1. Only one "transactional resource" (in this case, datasource) is involved.
    2. Only a single statement is executed on that datasource.
    3. No transaction-affecting operation (suspending a transaction, calling a non-@Atomic @Transactional method, ...) is executed.

The main thing I'm unsure of is how to handle this on the Narayana side. Can we temporarily set up the transaction manager so that it refuses to start a transaction when asked?


cc @mmusgrov @barreiro @gastaldi @Sanne @FroMage WDYT?

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/jdbcIssues related to the JDBC extensionskind/enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions