hibernate/hibernate-orm · error · ConnectionProviderConfigurationException

Could not configure Agroal: " + e.getMessage()

Error message

Could not configure Agroal: " + e.getMessage()

What it means

Thrown while translating a query that uses a FULL JOIN on a dialect without native full-join support (MySQL, MariaDB, Sybase/Sybase ASE, H2, TiDB). Hibernate rewrites the full join as a UNION of a left-join and a right-join branch, and to order that union it must append the order-by expressions as extra select items. When the query is DISTINCT, has a GROUP BY, or a HAVING clause, adding hidden select items would change the query result, so translation aborts with this UnsupportedOperationException.

Source

Thrown at hibernate-agroal/src/main/java/org/hibernate/agroal/internal/AgroalConnectionProvider.java:139

					new AgroalPropertiesReader( CONFIG_PREFIX ).readProperties( config );
			agroalProperties.modify()
					.connectionPoolConfiguration( cp -> cp.connectionFactoryConfiguration( cf -> {
				copyProperty( properties, JdbcSettings.DRIVER, cf::connectionProviderClassName, identity() );
				copyProperty( properties, JdbcSettings.URL, cf::jdbcUrl, identity() );
				copyProperty( properties, JdbcSettings.USER, cf::principal, NamePrincipal::new );
				copyProperty( properties, JdbcSettings.PASS, cf::credential, SimplePassword::new );
				copyProperty( properties, JdbcSettings.AUTOCOMMIT, cf::autoCommit, Boolean::valueOf );
				copyProperty( properties, JdbcSettings.LOGIN_TIMEOUT, cf::loginTimeout,
						value -> Duration.ofSeconds( Integer.parseInt( value ) ) );
				resolveIsolationSetting( properties, cf );
				return cf;
			} ) );

			agroalDataSource = AgroalDataSource.from( agroalProperties );
		}
		catch ( Exception e ) {
			CONNECTION_INFO_LOGGER.unableToInstantiateConnectionPool( e );
			throw new ConnectionProviderConfigurationException(
					"Could not configure Agroal: " + e.getMessage(),  e );
		}
	}

	private static Map<String,String> toStringValuedProperties(Map<String,Object> properties) {
		return properties.entrySet().stream()
				.collect( toMap( Map.Entry::getKey, e -> e.getValue().toString() ) );
	}

	// --- ConnectionProvider

	@Override
	public Connection getConnection() throws SQLException {
		return agroalDataSource == null ? null : agroalDataSource.getConnection();
	}

	@Override
	public void closeConnection(Connection connection) throws SQLException {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add every ORDER BY expression to the SELECT list (for distinct queries SQL requires the sort key to be selected anyway), e.g. 'select distinct a.id, b.name ... order by b.name'.
  2. Move the full join into a subquery and apply distinct/group-by plus ordering in the outer query, so the emulation no longer needs hidden select items.
  3. Order by an expression that is already selected (e.g. the grouping column) instead of a column of the full-joined side.
  4. Replace the full join with an explicit union of a left join and a right-join/anti-join written by hand, which you control fully.
  5. Upgrade Hibernate - the source carries a TODO stating this limitation could be removed, so newer versions may lift it.

Example fix

// before (MySQL/MariaDB): distinct + order by on non-selected column of full-joined side
List<Long> ids = session.createQuery(
    "select distinct a.id from A a full join a.items i order by i.label", Long.class)
    .getResultList();

// after: include the sort expression in the select list
List<Object[]> rows = session.createQuery(
    "select distinct a.id, i.label from A a full join a.items i order by i.label", Object[].class)
    .getResultList();
Defensive patterns

Strategy: try-catch

Validate before calling

// Before running: ensure every sort expression of a full-join distinct/grouped query is selected
boolean sortKeysSelected = orderByExpressions.stream()
    .allMatch( selectedExpressions::contains );
if ( !sortKeysSelected && ( query.isDistinct() || query.hasGroupBy() ) ) {
    throw new IllegalStateException("Add order-by expressions to the select list for full join emulation");
}

Try / catch

try {
    return em.createQuery(hql, type).getResultList();
} catch ( UnsupportedOperationException e ) {
    if ( e.getMessage() != null && e.getMessage().contains("Full join emulation") ) {
        // rewrite: select the sort keys or wrap the full join in a subquery
        return runFullJoinFallback( hql );
    }
    throw e;
}

Prevention

When it happens

Trigger: A full join (HQL 'full join' or Criteria full join) that is translated by MySQLSqlAstTranslator/MariaDBSqlAstTranslator/SybaseSqlAstTranslator/SybaseASESqlAstTranslator/H2SqlAstTranslator/TiDBSqlAstTranslator, combined with an ORDER BY expression that is NOT in the select list, while the query also uses select distinct, group by, or having. The check fires in emulateFullJoinWithUnion only when collectFullJoinEmulationExtraSelections returned at least one extra selection.

Common situations: Porting an application from PostgreSQL/Oracle (native FULL OUTER JOIN) to MySQL/MariaDB; keyset or offset pagination that sorts on a non-selected column (e.g. sort by a timestamp while selecting only ids) together with distinct/group-by; report queries with aggregations over full outer joins.

Related errors


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