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
3.02k stars 1.42k forks source link

NPE in HqlQueryTransformer.isSubquery for UPDATE HQL #3649

Closed dforrest-es closed 1 month ago

dforrest-es commented 1 month ago

With 3.2.11, repository initialization fails with

Caused by: java.lang.NullPointerException: Cannot invoke "org.antlr.v4.runtime.ParserRuleContext.getParent()" because "ctx" is null
    at org.springframework.data.jpa.repository.query.HqlQueryTransformer.isSubquery(HqlQueryTransformer.java:105) ~[spring-data-jpa-3.2.11.jar:3.2.11]
    at org.springframework.data.jpa.repository.query.HqlQueryTransformer.isSubquery(HqlQueryTransformer.java:105) ~[spring-data-jpa-3.2.11.jar:3.2.11]
    at org.springframework.data.jpa.repository.query.HqlQueryTransformer.isSubquery(HqlQueryTransformer.java:105) ~[spring-data-jpa-3.2.11.jar:3.2.11]
    at org.springframework.data.jpa.repository.query.HqlQueryTransformer.isSubquery(HqlQueryTransformer.java:105) ~[spring-data-jpa-3.2.11.jar:3.2.11]
    at org.springframework.data.jpa.repository.query.HqlQueryTransformer.isSubquery(HqlQueryTransformer.java:105) ~[spring-data-jpa-3.2.11.jar:3.2.11]
    at org.springframework.data.jpa.repository.query.HqlQueryTransformer.isSubquery(HqlQueryTransformer.java:105) ~[spring-data-jpa-3.2.11.jar:3.2.11]
    at org.springframework.data.jpa.repository.query.HqlQueryTransformer.visitVariable(HqlQueryTransformer.java:362) ~[spring-data-jpa-3.2.11.jar:3.2.11]
    at org.springframework.data.jpa.repository.query.HqlQueryRenderer.visitVariable(HqlQueryRenderer.java:1) ~[spring-data-jpa-3.2.11.jar:3.2.11]

Issue is very similar to #2977 but for update clauses:

update MyEntityStaging AS mes
set mes.col = 'test'
where mes.id = 1

Entity:

@Entity
@NamedQuery(name = "MyEntity.updateCol", query = """
        update MyEntity AS mes
        set mes.col = 'test'
        where mes.id = 1
""")
class MyEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Long id;

    private String col;
}

Repository:

interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
    @Modifying
    @Transactional
    void updateCol();
}

Reproducer: demo.zip Extract and run ./mvnw spring-boot:run to observe the exception. In the pom.xml, change the spring-data-jpa.version from 3.2.11 to 3.2.10 and run again, now it works.


My investigation:

Specifically, this change causes HqlQueryTransformer#isSubquery(ParserRuleContext) to be run for update clauses & likely needs the same fix from #2977 applied for HqlParser.UpdateStatementContext (see commit 6b9d3e2)

Wrokaround: Removing the AS from the table alias bypass this error.

christophstrobl commented 1 month ago

Thank you @dforrest-es for reporting and analysis.

klauss42 commented 1 month ago

The issue is also reproducible when switching from Spring Boot 3.3.4 to 3.3.5. If using the following pom in the example project from above:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>demo</description>
    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

When switching back to 3.3.4 the app starts up properly.

In my application I encountered the same issue with a @Query annotation:

@Transactional
  @Modifying
  @Query(
    """
      delete from ActivityDetailProjection a
        where a.activityDetailId.customerId= :customerId and a.activityDetailId.vendorId = :vendorId
    """
  )
  fun deleteByVendorId(
    customerId: String,
    vendorId: String,
  )
ThomHurks commented 3 weeks ago

Many of our services also had failed builds when updating to Spring Boot 3.3.5 due to this bug. The workaround is to put this in your POM 's properties:

<spring-data-bom.version>2024.0.4</spring-data-bom.version>

this will use the previous version of spring-data-jpa that works.

dforrest-es commented 3 weeks ago

Thanks for a quick fix @mp911de & @christophstrobl!

Fabio1988 commented 2 weeks ago

Is there any ETA of releasing that fix?

mp911de commented 2 weeks ago

We do, check out the Release Calender for most recent schedule updates in accordance with the assigned milestone.