**Affected versions:** 7.4.0.Final – 7.4.11.Final (latest tested). Works on 7.3.2.Final, so this is a 7.4 regression.
#### Description
I have discovered an issue while developing a library for my company. A JPQL query that compares several entity attributes against **one single named parameter** fails to bind when
* the attributes are mapped with the same `@Convert(converter = ...)`, whose relational type is not one of the types
`BindingTypeHelper.resolveTimestampTemporalTypeVariant` knows, and
* the parameter is used **three or more times** across **at least two different** such attributes.
For example the following query:
```java
entityManager.createQuery("""
SELECT r FROM Record r
WHERE r.validFrom <= :now
AND r.validTo > :now
AND r.recordedFrom <= :now
""", Record.class)
.setParameter("now", OffsetDateTime.now())
.getResultList();
```
fails with
```
java.lang.IllegalArgumentException: Unsure how to handle given Java type [java.time.LocalDateTime]
as TemporalType#TIMESTAMP
at org.hibernate.type.internal.BindingTypeHelper.resolveTimestampTemporalTypeVariant(BindingTypeHelper.java:144)
at org.hibernate.type.internal.BindingTypeHelper.resolveBindType(BindingTypeHelper.java:110)
at org.hibernate.sql.exec.spi.JdbcParameterBindings.createAndAddBinding(JdbcParameterBindings.java:97)
at org.hibernate.sql.exec.internal.AbstractJdbcParameter.forEachDisassembledJdbcValue(AbstractJdbcParameter.java:184)
at org.hibernate.metamodel.mapping.Bindable.forEachJdbcValue(Bindable.java:173)
at org.hibernate.sql.exec.spi.JdbcParameterBindings.registerParametersForEachJdbcValue(JdbcParameterBindings.java:77)
at org.hibernate.query.sqm.internal.SqmUtil.createValueBindings(SqmUtil.java:724)
at org.hibernate.query.sqm.internal.SqmUtil.createJdbcParameterBinding(SqmUtil.java:657)
at org.hibernate.query.sqm.internal.SqmUtil.createJdbcParameterBindings(SqmUtil.java:532)
...
```
The mapping looks as follows:
```java
@Converter
public class UtcOffsetConverter implements AttributeConverter<OffsetDateTime, LocalDateTime> { ... }
@Entity
public class Record {
@Convert(converter = UtcOffsetConverter.class) private OffsetDateTime validFrom;
@Convert(converter = UtcOffsetConverter.class) private OffsetDateTime validTo;
@Convert(converter = UtcOffsetConverter.class) private OffsetDateTime recordedFrom;
@Convert(converter = UtcOffsetConverter.class) private OffsetDateTime recordedTo;
}
```
#### What exactly triggers it
Below is a small table summarising when the exception gets triggered:
| Query | Result |
| --- | --- |
| `validFrom <= :now` | works |
| `validFrom <= :now AND validTo > :now` | works |
| `validFrom <= :now AND validTo > :now AND recordedFrom <= :now` | **fails** |
| `validFrom <= :now AND validTo > :now AND validFrom <= :now` | **fails** |
| `validFrom <= :now` repeated 4× (same attribute) | works |
| `validFrom <= :a AND validTo > :b AND recordedFrom <= :c` | works |
So: **≥ 3 uses AND ≥ 2 distinct attributes.** One attribute repeated any number of times is fine, and splitting the one parameter into several distinct ones is fine.
#### Suggested fix
**Method:** `org.hibernate.query.internal.QueryParameterBindingImpl#areTypesCompatible`
Use equality instead of comparison by identity:
```diff
private boolean areTypesCompatible(BasicValuedMapping mapping1, BasicValuedMapping mapping2) {
final JdbcMapping jdbcMapping1 = mapping1.getSingleJdbcMapping();
final JdbcMapping jdbcMapping2 = mapping2.getSingleJdbcMapping();
return jdbcMapping1.getJdbcType() == jdbcMapping2.getJdbcType()
- && jdbcMapping1.getValueConverter() == jdbcMapping2.getValueConverter();
+ && Objects.equals( jdbcMapping1.getValueConverter(), jdbcMapping2.getValueConverter() );
}
```
`AttributeConverterBean` already overrides `equals`/`hashCode` (since 7.3, over
`attributeConverterBean`, `converterJavaType`, `domainJavaType` and `jdbcJavaType`), so two converter beans for the same converter class and the same domain/relational types compare equal.
Regards,
Daniele