hibernate/hibernate-orm · error · MappingException

many-to-many defining filter or where without join fetching

Error message

many-to-many defining filter or where without join fetching is not valid within collection [%s] using join fetching

What it means

For a many-to-many collection element that declares <filter> elements or a where= restriction, Hibernate must apply that constraint inside the same SQL as the collection load. When the collection binding's fetch style is JOIN but the element (many-to-one) binding resolved to a non-JOIN fetch (typically fetch='select' on the <many-to-many>), the constraint cannot be applied and binding fails.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java:3215

					getNonEmptyOrConjunctionIfBothNonEmpty(
							referencedEntityBinding.getWhere(),
							elementSource.getWhere()
					)
			);
			collectionBinding.setManyToManyOrdering( elementSource.getOrder() );
			bindManyToManyFilters( elementSource, mappingDocument, collectionBinding, elementBinding );
		}

		private void bindManyToManyFilters(
				PluralAttributeElementSourceManyToMany elementSource,
				MappingDocument mappingDocument,
				Collection collectionBinding,
				ManyToOne elementBinding) {
			if ( !isEmpty( elementSource.getFilterSources() )
					|| elementSource.getWhere() != null ) {
				if ( collectionBinding.getFetchStyle() == FetchStyle.JOIN
						&& elementBinding.getFetchStyle() != FetchStyle.JOIN ) {
					throw new MappingException(
							"many-to-many defining filter or where without join fetching is not "
							+ "valid within collection [%s] using join fetching"
									.formatted( getPluralAttributeSource().getAttributeRole().getFullPath() ),
							mappingDocument.getOrigin()
					);
				}
			}

			for ( var filterSource : elementSource.getFilterSources() ) {
				bindManyToManyFilter( mappingDocument, collectionBinding, filterSource );
			}
		}

		private void bindManyToManyFilter(
				MappingDocument mappingDocument, Collection collectionBinding, FilterSource filterSource) {
			final String name = filterSource.getName();
			if ( name == null ) {
				if ( BOOT_LOGGER.isTraceEnabled() ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Remove fetch='select' from the <many-to-many> element so it follows the collection's join fetching
  2. Or switch the collection itself to fetch='select' (or default) so the filter/where can be applied in a separate select
  3. Or drop the where/filters on the element if join fetching the collection matters more

Example fix

// before
<set name='roles' fetch='join'>
    <key column='user_id'/>
    <many-to-many class='Role' fetch='select' where="deleted = false"/>
</set>

// after
<set name='roles' fetch='join'>
    <key column='user_id'/>
    <many-to-many class='Role' where="deleted = false"/>
</set>
Defensive patterns

Strategy: validation

Validate before calling

NodeList m2ms = doc.getElementsByTagName("many-to-many");
for (int i = 0; i < m2ms.getLength(); i++) {
    Element m = (Element) m2ms.item(i);
    boolean hasFilterOrWhere = m.getElementsByTagName("filter").getLength() > 0 || m.getAttributeNode("where") != null;
    Element collection = (Element) m.getParentNode();
    boolean collectionJoin = "join".equals(collection.getAttribute("fetch"));
    boolean elementSelect = "select".equals(m.getAttribute("fetch"));
    if (hasFilterOrWhere && collectionJoin && elementSelect) {
        throw new IllegalStateException("filtered many-to-many inside join-fetched collection must not use fetch='select'");
    }
}

Try / catch

catch (MappingException e) at bootstrap; the message names the collection. Remove fetch='select' from the many-to-many or switch the collection off join fetching so the filter/where can be applied.

Prevention

When it happens

Trigger: <bag name='...' fetch='join'><many-to-many class='...' fetch='select' where='...'/></bag>, or a <many-to-many> containing <filter> elements while the collection is join-fetched and the element fetch was overridden to select.

Common situations: Tuning fetch strategies per association and overriding the element's fetch to select; adding row-level filters/where clauses to existing join-fetched collections; copying filter definitions from select-fetched sets into join-fetched bags.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/ceb79c97b71a9715. Report an issue: GitHub.