spring-projects/spring-data-jpa

DTO query rewriting renders invalid queries

Closed this issue ยท 11 comments

I am seeing this error with spring-data 3.5.0:

INFO  [2025-05-19 17:36:30,602] [] [kafka-producer-network-thread | producer-2] org.apache.kafka.clients.producer.internals.TransactionManager: [Producer clientId=producer-2] ProducerId set to 1 with epoch 0
java.lang.IllegalArgumentException: org.hibernate.query.SyntaxException: At 1:50 and token ')', no viable alternative at input 'SELECT new com.adswizz.domain.entity.CampaignDeal(*) FROM CampaignDeal cd LEFT JOIN FETCH cd.dealLibrary d LEFT JOIN FETCH d.publisher p WHERE cd.campaignId = :campaignId' [SELECT new com.adswizz.domain.entity.CampaignDeal() FROM CampaignDeal cd LEFT JOIN FETCH cd.dealLibrary d LEFT JOIN FETCH d.publisher p WHERE cd.campaignId = :campaignId]

when running the following query:

@Query("SELECT cd FROM CampaignDeal cd "
            + "LEFT JOIN FETCH cd.dealLibrary d "
            + "LEFT JOIN FETCH d.publisher p "
            + "WHERE cd.campaignId = :campaignId")`
List<CampaignDeal> findCampaignDealsForCampaign(Integer campaignId);

The entity definition is:

@Entity
@Table(name = "campaign_deals")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CampaignDeal {

    @EmbeddedId
    private CampaignDealId campaignDealId;

    @Column(name = "campaign_id", insertable = false, updatable = false)
    private Integer campaignId;

    @Column(name = "deal_library_id")
    private Integer dealLibraryId;

    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "deal_library_id", updatable = false, insertable = false, referencedColumnName = "id")
    private Deal dealLibrary;

    @Column(name = "publisher_id")
    private Integer publisherId;

    @ToString.Exclude
    @EqualsAndHashCode.Exclude
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "publisher_id", updatable = false, referencedColumnName = "affiliateid", insertable = false)
    private Publisher publisher;

    @Column(name = "type")
    @Enumerated(EnumType.STRING)
    private Type type;

    @Embeddable
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class CampaignDealId implements Serializable {

        private static final long serialVersionUID = 5373763488252554888L;

        @ManyToOne(fetch = FetchType.LAZY, targetEntity = Campaign.class)
        @JoinColumn(name = "campaign_id")
        @ToString.Exclude
        @EqualsAndHashCode.Exclude
        private Campaign campaign;

        @Column(name = "deal_id") //the dealID string, not the fk to deal_library
        private String dealId;

    }

    public enum Type {
        STRING,
        LIBRARY
    }
}

This used to work just fine until upgrading to the 3.5.0 version.

Same issue after upgrading from 3.5.0

thank you for reporting - we'll have a look

I get a similar issue with a @query method that exists in a repo for a different entity. It fails with spring boot 3.5.0 but works if you change the version in the pom to spring boot 3.4.6. Attached a minimal example.

demo.zip

I faced the same problem and solved it by changing the query in your case from
@Query("SELECT cd FROM CampaignDeal cd ")
to
@Query("FROM CampaignDeal cd ")

I faced the same problem and solved it by changing the query in your case from @Query("SELECT cd FROM CampaignDeal cd ") to @Query("FROM CampaignDeal cd ")

Thanks @Jackpad - indeed, seems to work in my case as well.

@Jackpad @dev-ducu Thanks. Works for us as well.

We have several other queries across different repositories with @Query("SELECT s FROM Something s..."), that works quite fine and are upgraded to Spring Boot 3.5.0 without errors.

Copying from #3898, the breaking commit here is 1341c3f

For the time being, we've identified two issues:

  1. When using classes with multiple constructors, we back off from determining projection properties and therefore we render an invalid empty constructor (SELECT new com.adswizz.domain.entity.CampaignDeal(), the asterisk marks the unexpected parts in the query)
  2. Additionally, when returning JPA entities in a query that does not match the repository type (e.g. CampaignRepository extends Repositor<Campaign, โ€ฆ>, List<CampaignDeal> findCampaignDealsForCampaign(Integer campaignId);), then the returned type is considered a projection. In that case, we also rewrite the query to a DTO projection.

While the second case is technically a projection, I think we should back off if the returned type is a JPA-managed type.

I've pushed a bugfix branch and our CI has published artifacts containing that change with the version 3.5.x-GH-3895-SNAPSHOT.

Care to upgrade to version 3.5.x-GH-3895-SNAPSHOT, available from https://repo.spring.io/snapshot/?

<dependencies>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-jpa</artifactId>
        <version>3.5.x-GH-3895-SNAPSHOT</version>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>spring-snapshot</id>
        <url>https://repo.spring.io/snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

Hi @mp911de - I tested this snapshot and it looks good. All our regression tests are green now.

Thanks @mp911de - I can confirm this snapshot release fixes our pipeline as well.