Skip to content

JPQL bulk UPDATE drops fully-qualified enum literal in SET clause when target field is unqualified (no alias) #2758

Description

@renatsaf

Summary

In a JPQL bulk UPDATE, a fully-qualified enum literal used as the new value of a SET item is silently dropped from the generated SQL when the target state field is referenced without an identification variable (alias). The resulting SQL is malformed (SET <COL> = WHERE ...) and fails at the database.

The same query works correctly if the entity is given an alias and the field is qualified with it. Non-enum literals (e.g. strings) work fine even without an alias. So the failure is specific to the combination unqualified SET target field + fully-qualified enum literal.

This was originally reported against GlassFish / Jakarta Data, where alias-less queries (JDQL style) are idiomatic and are forwarded verbatim to EntityManager.createQuery(...): eclipse-ee4j/glassfish#26015

Environment

  • EclipseLink 5.0.0 (build 5.0.0.v202603230926)
  • JDK 21
  • Database: H2 2.3.230 (DB-independent — failure is at JPQL→SQL generation, before execution)

Entity

package com.example.domain;

import jakarta.persistence.*;

@Entity
public class Todo {
    @Id
    Long id;
    String title;
    @Enumerated(EnumType.STRING)
    Status status;
    // getters/setters omitted
}

public enum Status { PENDING, COMPLETED }

Reproduction (results)

Running each statement via em.createQuery(jpql).setParameter("id", 1L).executeUpdate():

JPQL Result
update Todo set status=com.example.domain.Status.PENDING where id=:id FAIL
update Todo t set t.status=com.example.domain.Status.PENDING where t.id=:id ✅ OK (status set to PENDING)
update Todo set title='done' where id=:id ✅ OK

Expected

The first statement should resolve com.example.domain.Status.PENDING to the enum literal and update the row, identical to the aliased form.

Actual

EclipseLink generates SQL with an empty new value:

UPDATE TODO SET STATUS =  WHERE (ID = ?)

which the database rejects, e.g. (H2):

org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement
"UPDATE TODO SET STATUS =  [*]WHERE (ID = ?)"

The enum literal is correctly resolved and emitted only when the target field is qualified by an identification variable (case 2 above).

Minimal standalone reproduction

persistence.xml (RESOURCE_LOCAL, H2 in-memory):

<persistence xmlns="https://jakarta.ee/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="https://jakarta.ee/xml/ns/persistence https://jakarta.ee/xml/ns/persistence/persistence_3_2.xsd"
             version="3.2">
  <persistence-unit name="pu" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>com.example.domain.Todo</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
      <property name="jakarta.persistence.jdbc.driver" value="org.h2.Driver"/>
      <property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
      <property name="jakarta.persistence.jdbc.user" value="sa"/>
      <property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
    </properties>
  </persistence-unit>
</persistence>

Driver/runner:

import jakarta.persistence.*;

public class Repro {
    public static void main(String[] a) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("pu");
        // seed one row with id=1 first (persist a Todo), then:
        try {
            EntityManager em = emf.createEntityManager();
            em.getTransaction().begin();
            em.createQuery("update Todo set status=com.example.domain.Status.PENDING where id=:id")
              .setParameter("id", 1L)
              .executeUpdate();   // throws: SET STATUS =  WHERE ...
            em.getTransaction().commit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Notes / impact

  • Reproduced layer is purely EclipseLink's Hermes JPQL parser (UpdateQueryVisitor / ExpressionBuilderVisitor$PathResolver enum-literal resolution); no other framework involved.
  • This blocks Jakarta Data @Query repository methods that set an enum via a literal, because JDQL omits the alias by convention.
  • Workarounds: qualify the field with an alias, or bind the value as a parameter instead of an inline literal.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions