Hi!
(Hibernate 5.6.9.Final)
I’m experiencing problems while trying to build a query with Criteria builder.
My query works fine if I use only map not trying to target key()
or value()
path modifiers.
But when I introduce key()
to my map, there is exception thrown telling me ‘Could not locate CollectionPersister for role : com.thevegcat.app.localization.Localizations.data’.
After adding breakpoint to MetamodelImpl.java
@ line 691 where exception is thrown, I found role
parameter has wrong value. Value stated in exception is path to Localizations
class, but it should be path to Localizations
object inside City
entity.
When I inspect things inside MetamodelImpl.java
, I see class variable Map<String,CollectionPersister> collectionPersisterMap
.
Inside this map I can find an entry with key com.thevegcat.app.entities.city.City.localizations.data
which is OK, but no entry is found for key stated in exception.
It seems adding key()
modifier to MapJoin
object calculates a path from embedded class Localizations
but it should get a path from root class City
.
public List<City> findBySubstring(String name) {
CriteriaBuilder cb = this.entityManager.getCriteriaBuilder();
CriteriaQuery<City> q = cb.createQuery(City.class);
Root<City> root = q.from(City.class);
MapJoin<City, String, String> locMap =
root.join("localizations").joinMap("data", JoinType.LEFT);
Predicate nativeNamePredicate = cb.like(root.get("nativeName"), name);
Predicate localizationsPredicate = cb.like(locMap, name);
// !!! Predicate localizationsPredicate = cb.like(locMap.key(), name);
q
.select(root)
.distinct(true)
.where(
cb.or(
nativeNamePredicate,
localizationsPredicate
)
);
TypedQuery<City> tq = this.entityManager.createQuery(q);
tq.setMaxResults(10);
return tq.getResultList();
}
City.java
package com.thevegcat.app.entities.city;
@Entity
public class City extends AbstractLocEntity {
@Getter @Setter
private String nativeName;
}
AbstractLocEntity.java
package com.thevegcat.app.base.entity;
@MappedSuperclass
public abstract class AbstractLocEntity extends AbstractBaseEntity {
@Getter @Setter
@Embedded
private Localizations localizations = new Localizations();
}
Localizations.java
package com.thevegcat.app.localization;
@Getter
@Embeddable
public class Localizations {
@ElementCollection
@MapKeyColumn(name = "tkey", nullable = false)
@Column(name = "locVal", columnDefinition = "TEXT")
private Map<String, String> data;
}
Original query
@Query(
"SELECT DISTINCT c " +
"FROM City c " +
"LEFT JOIN FETCH c.localizations.data loc " +
"WHERE " +
"LOWER( c.nativeName ) LIKE LOWER( :name ) " +
"OR (" +
"KEY( loc ) LIKE 'NAME%' " +
"AND " +
"LOWER( loc ) LIKE LOWER( :name ) " +
")"
)