Description
When using the set(Path, value) style on JPAInsertClause (or HibernateInsertClause) instead of columns().values(), the JPQL produced by JPQLSerializer.serializeForInsert is malformed. Hibernate then rejects it with SyntaxException.
Reproduction
QCat cat = QCat.cat;
new JPAInsertClause(em, cat)
.set(cat.name, "Bobby")
.set(cat.alive, false)
.execute();
Actual JPQL emitted
insert into Cat (name, alive)
cat.name = ?1, cat.alive = ?2
This is not valid JPQL/HQL. After the column list, Hibernate expects VALUES (...) or a sub-select, but the serializer emits path = value pairs (a SET-style assignment that JPQL/HQL does not support).
Expected JPQL
insert into Cat (name, alive)
values (?1, ?2)
Stack trace
org.hibernate.query.SyntaxException: At 2:0 and token 'cat', mismatched input 'cat',
expecting one of the following tokens: '(', FROM, ORDER, SELECT, VALUES, WHERE, WITH
[insert into Cat (name, alive)
cat.name = ?1, cat.alive = ?2]
at org.hibernate.query.hql.internal.StandardHqlTranslator$1.syntaxError(StandardHqlTranslator.java:124)
...
at com.querydsl.jpa.impl.JPAInsertClause.execute(JPAInsertClause.java:84)
Root cause
JPQLSerializer.serializeForInsert (querydsl-jpa) handles the inserts map (the set() style) by emitting path = value pairs:
} else if (inserts != null && !inserts.isEmpty()) {
first = true;
for (Map.Entry<Path<?>, Expression<?>> entry : inserts.entrySet()) {
if (!first) append(", ");
handle(entry.getKey());
append(" = ");
handle(entry.getValue());
first = false;
}
}
JPQL/HQL INSERT only supports the VALUES form and the ... SELECT form. There is no SET form. The serializer should convert the inserts map to a VALUES (...) list of values in the same order as the column list it just emitted.
Suggested fix
Replace the SET-style emission in JPQLSerializer.serializeForInsert with a VALUES list:
} else if (inserts != null && !inserts.isEmpty()) {
append(VALUES);
append(" (");
first = true;
for (Map.Entry<Path<?>, Expression<?>> entry : inserts.entrySet()) {
if (!first) append(", ");
handle(entry.getValue());
first = false;
}
append(")");
}
The column list (already emitted earlier from inserts.keySet()) and the value list will line up by iteration order of the LinkedHashMap used by JPAInsertClause/HibernateInsertClause.
Affected
- com.querydsl.jpa.impl.JPAInsertClause#execute (whenever any set(...) is used)
- com.querydsl.jpa.hibernate.HibernateInsertClause#execute (same)
- Any caller relying on set()-style JPA INSERT
columns().values() style and INSERT ... SELECT (subQuery) are unaffected.
Environment
- querydsl-jpa: 7.x (current master)
- Hibernate: 6.x
- Java: 17+
Workaround
Use columns(...).values(...) instead of repeated set(path, value) calls.
Description
When using the
set(Path, value)style onJPAInsertClause(orHibernateInsertClause) instead ofcolumns().values(), the JPQL produced byJPQLSerializer.serializeForInsertis malformed. Hibernate then rejects it withSyntaxException.Reproduction
Actual JPQL emitted
This is not valid JPQL/HQL. After the column list, Hibernate expects VALUES (...) or a sub-select, but the serializer emits path = value pairs (a SET-style assignment that JPQL/HQL does not support).
Expected JPQL
Stack trace
org.hibernate.query.SyntaxException: At 2:0 and token 'cat', mismatched input 'cat',
expecting one of the following tokens: '(', FROM, ORDER, SELECT, VALUES, WHERE, WITH
[insert into Cat (name, alive)
cat.name = ?1, cat.alive = ?2]
at org.hibernate.query.hql.internal.StandardHqlTranslator$1.syntaxError(StandardHqlTranslator.java:124)
...
at com.querydsl.jpa.impl.JPAInsertClause.execute(JPAInsertClause.java:84)
Root cause
JPQLSerializer.serializeForInsert (querydsl-jpa) handles the inserts map (the set() style) by emitting path = value pairs:
JPQL/HQL INSERT only supports the VALUES form and the ... SELECT form. There is no SET form. The serializer should convert the inserts map to a VALUES (...) list of values in the same order as the column list it just emitted.
Suggested fix
Replace the SET-style emission in JPQLSerializer.serializeForInsert with a VALUES list:
The column list (already emitted earlier from inserts.keySet()) and the value list will line up by iteration order of the LinkedHashMap used by JPAInsertClause/HibernateInsertClause.
Affected
columns().values() style and INSERT ... SELECT (subQuery) are unaffected.
Environment
Workaround
Use columns(...).values(...) instead of repeated set(path, value) calls.