spring-bootkotlinspring-data-jpa

Spring data JPA returns unexpected result with scroll based query and operation precedence


I have these entities

@Entity
@Table(name = "Item")
class Item(
    @Column(name = "name", nullable = false)
    var name: String,

    @Column(name = "added_date", columnDefinition = "TIMESTAMP WITH TIME ZONE", nullable = true)
    var addedDate: ZonedDateTime? = null,

    @Column(name = "duration", nullable = true)
    var duration: Int? = null,

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "head_of_id", nullable = false)
    val headOf: HeadOf,
) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = 0
}
@Entity
@Table(name = "head_of")
class HeadOf(
    @Column(name = "company", nullable = false)
    var company: String,

    @Column(name = "city", nullable = false)
    var city: String,

    @Column(name = "age", nullable = true)
    var age: Int?
) { 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long = 0
}

and this is the corresponding repository

interface ItemRepository : JpaRepository<Item, Long> {
    fun findByHeadOfIdAndAddedDateNotNullOrDurationNotNull(
        headOfId: Long,
        position: ScrollPosition,
        pageable: Pageable,
    ): Window<Item>
}

This is what is currently in the table Item. Its one row (its the result of a dump)

INSERT INTO public.item VALUES (1, 'Some fancy title', '2024-12-31 07:14:42.955789+00', NULL, 1000);

As you can see, headOf is 1000L.

Now, when I call this function findByHeadOfIdAndAddedDateNotNullOrDurationNotNull with the following values

headOfId: 0 scrollPosition: ScrollPosition.of(...) PageRequest.of(0, 10)

it returns me that one row that is in there. I do not expect that, because the headOfId I am passing is 0L and not 1000L.

I cannot use a @Query annotation, then I get this

Could not create query for public abstract org.springframework.data.domain.Window org...jpa.repository.ItemRepository.findByHeadOfIdAndAddedDateNotNullOrDurationNotNull(long,org.springframework.data.domain.ScrollPosition,org.springframework.data.domain.Pageable); Reason: Scroll queries are not supported using String-based queries

Is there a solution to this?


Solution

  • The problem is operator precedence in SQL. It gets evaluated like this

    SELECT ... FROM ... WHERE (headOfId === :headOfId AND addedDate NOT NULL) OR duration NOT NULL
    

    The soultion to this is to write the query in this way (which is ugly, but it works)

    interface ItemRepository : JpaRepository<Item, Long> {
        fun findByHeadOfIdAndAddedDateNotNullOrHeadOfIdAndDurationNotNull(
            headOfId1: Long,
            headOfId2: Long,
            position: ScrollPosition,
            pageable: Pageable,
        ): Window<Item>
    }
    

    You also might be able to do it with the Criteria API.