Skip to content

Derived queries on snake_case mapped columns trigger repeated PropertyReferenceException lookups and create avoidable overhead #2335

Description

@RipulHandoo

Environment

  • Java 25
  • Spring Boot 3.5.x
  • Spring Data R2DBC 3.5.x
  • Oracle Database

Description
When an entity uses camelCase Java properties mapped to snake_case database columns, derived query execution appears to perform repeated failed property lookups for each referenced column. These failures are handled internally and discarded, but they still incur exception construction and stack-trace overhead.

For example, a derived method such as findByCustomerIdIn(...) on an entity with a customerId property and a customer_id column appears to cause the query mapper to attempt resolving customer_id as a Java property, fail with PropertyReferenceException, and then fall back to treating it as a raw column name.

Impact
This creates avoidable overhead on every derived query execution. In profiling, a significant amount of CPU time is spent inside exception creation and stack-trace generation, even though the exception is expected and immediately handled.

Observed behavior

  • Each relevant WHERE-clause column appears to trigger a failed property-resolution attempt.
  • The exception is caught internally and used as control flow.
  • The behavior is correct functionally, but expensive at runtime.

Expected behavior
The query mapper should first check whether the input matches a known persistent property before attempting exception-based resolution, avoiding exception creation for raw column names.

Why @Column does not help
The lookup appears to happen before @Column metadata is consulted, so annotating the field does not prevent the failed property-resolution attempt.

Suggested improvement
Prefer a non-exception-based check for known persistent properties before calling PropertyPath.from(...), or otherwise cache/short-circuit the raw-column case.

Workaround
Using explicit @Query methods avoids this path.

Minimal example

@Table("orders")
public class Order {
    @Id
    private Long orderId;
    private Long customerId;
    private String status;
}

public interface OrderRepository extends ReactiveCrudRepository<Order, Long> {
    Flux<Order> findByCustomerIdIn(List<Long> customerIds);
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions