Possible bug in Criteria API?

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 ) " +
		")"
)

Sounds like a bug, yes. Would appreciate if you could create a JIRA issue for this with a reproducer. Note that this issue is most likely gone in Hibernate 6.0 already.

1 Like

Thanks!
Month ago tried to switch to Hibernate 6, but as I use BlazeBit Blaze-Persistence and Hibernate Search, things became extremelly complex and I decided to stay with latest Hibernate 5 update.
There are some queries that works with Blaze-Persistence and the same query creates invalid SQL when query-dsl is used and that’s why I stick to Blaze-Persistence.

You’re welcome. When I get back from my vacation in August, I’ll finish the Hibernate 6 integration in Blaze-Persistence and then you will be able to switch to the latest Hibernate as well :slight_smile: