spring-projects / spring-data-jpa

Simplifies the development of creating a JPA-based data access layer.
https://spring.io/projects/spring-data-jpa/
Apache License 2.0
2.92k stars 1.39k forks source link

Error when using keyword queries which reference properties on a base class #3492

Closed nanafan93 closed 4 weeks ago

nanafan93 commented 1 month ago

Context I have 2 entities A and B where A extends B. The inheritance type is JOINED.

I have a repository for A (the sub class) which has methods like:

fun existsByPropertyXIdAndIsReversedIsFalse(xId: Int): Boolean //1
fun findByPropertyXAndIsReversed(x: X, reversed: Boolean): List<A> //2

It recognises the fact that IsReversed is a property on the base class. (Since the application doesn't even start if the property is named badly.) However, executing these queries yields completely different results.

Problem

  1. In case of query 1, it throws an exception complaining about an unknown column on the base type (isReversed). The generated SQL doesn't even attempt to join the two tables. select * from A .... A.isReversed = false ... limit
  2. In case of query 2, the SQL it generates joins the 2 tables together properly and doesn't result in an error. select * from A join B .... B.isReversed = ? ...

Expected Behavior Query 1 should attempt to join the sub class table to the base class table and look for the correct property on the correct table without erroring.

nanafan93 commented 1 month ago

After some more experimentation, this query works as well

fun existsByXAndIsReversedFalse(x: X): Boolean.

So, If I pass in a full object reference (X) instead of a nested property like id (X.Id) then it works.

mp911de commented 4 weeks ago

Care to attach a minimal reproducer so that we can diagnose the issue quicker and more precise?

nanafan93 commented 4 weeks ago

TLDR: I will close this issue as its behaving as expected. Sorry, I should have done this to begin with 😞 Here is how I tried to reproduce it.

package com.example.demo

import jakarta.persistence.*
import org.springframework.boot.CommandLineRunner
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication
import org.springframework.context.annotation.Bean
import org.springframework.data.jpa.repository.JpaRepository

@SpringBootApplication
class DemoApplication {
    @Bean
    fun runner(repository: EmployeeRepo): CommandLineRunner {
        return CommandLineRunner { args: Array<String?>? ->
             repository.existsByDepartmentIdAndIsHandsomeFalse(25)
            println("I was expecting the above to crash but its fine")
        }
    }
}

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

interface SomeInterface {
    val id: Int
}

@Entity
@Table(name = "person")
@Inheritance(strategy = InheritanceType.JOINED)
open class Person: SomeInterface {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    override var id = 0

    @Column(name = "handsome")
    var isHandsome = false
}

@Entity
@Table(name = "employee")
open class Employee : Person() {

    @ManyToOne(fetch = FetchType.LAZY)
    var department: Department? = null
}

@Entity
open class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    var id: Long? = null
    var name: String? = null

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "department")
    var employees: MutableList<Employee> = mutableListOf()

}

interface EmployeeRepo : JpaRepository<Employee?, Long?> {
    fun existsByDepartmentIdAndIsHandsomeFalse(departmentId: Long?): Boolean
}

This generates the following SQL

Hibernate: select e1_0.id from employee e1_0 join person e1_1 on e1_0.id=e1_1.id where e1_0.department_id=? and not(e1_1.handsome) fetch first ? rows only

This is working as expected but it does not work in my project with the same setup. Does the repository ultimately depend on the JDBC driver to generate the SQL ? We are using an extremely outdated version of mysql. The SQL I get in my project is the follow. It is missing the join to the "base" table and also crashes with a JDBC exception.

select n1_0.id from employee n1_0 where n1_0.department_id=? and not(n1_1.handsome) limit ?