hibernate/hibernate-orm · error · IllegalArgumentException

not a BasicPluralJavaType

Error message

not a BasicPluralJavaType

What it means

ArrayJdbcType (and dialect subclasses such as OracleArrayJdbcType) derives the element Java type from the domain descriptor via the static elementJavaType() helper. That helper accepts only the Byte[]/byte[] special cases and descriptors implementing BasicPluralJavaType; anything else throws IllegalArgumentException('not a BasicPluralJavaType'). The message signals that the domain Java type and the array JDBC type disagree - the domain type is not perceived as an array/collection.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/ArrayJdbcType.java:93

			final var parameterizedType =
					new ParameterizedTypeImpl( javaType.getJavaTypeClass(),
							new Type[0], null );
			return javaType.createJavaType( parameterizedType, typeConfiguration );
		}
	}

	protected static JavaType<?> elementJavaType(JavaType<?> javaTypeDescriptor) {
		if ( javaTypeDescriptor instanceof ByteArrayJavaType
			|| javaTypeDescriptor instanceof PrimitiveByteArrayJavaType ) {
			// Special handling needed for Byte[] and byte[],
			// because that would conflict with the VARBINARY mapping
			return ByteJavaType.INSTANCE;
		}
		else if ( javaTypeDescriptor instanceof BasicPluralJavaType<?> basicPluralJavaType ) {
			return basicPluralJavaType.getElementJavaType();
		}
		else {
			throw new IllegalArgumentException("not a BasicPluralJavaType");
		}
	}

	@Override
	public <T> JdbcLiteralFormatter<T> getJdbcLiteralFormatter(JavaType<T> javaTypeDescriptor) {
		return new JdbcLiteralFormatterArray<>( javaTypeDescriptor,
				elementJdbcType.getJdbcLiteralFormatter( elementJavaType( javaTypeDescriptor ) ) );
	}

	@Override
	public Class<?> getPreferredJavaTypeClass(WrapperOptions options) {
		return Object[].class;
	}

	protected String getElementTypeName(JavaType<?> javaType, SharedSessionContractImplementor session) {
		// TODO: ideally, we would have the actual size or the actual type/column accessible
		//       this is something that we would need for supporting composite types anyway
		if ( elementJdbcType instanceof StructuredJdbcType structJdbcType ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Make the custom descriptor implement BasicPluralJavaType<T> and return the element type from getElementJavaType().
  2. Fix the mapping: use array JDBC types only for array/collection attributes (@JdbcTypeCode(SqlTypes.ARRAY) or @Array), keep scalars on scalar JDBC types.
  3. For byte[]/Byte[]-backed types, ensure the registered descriptors are PrimitiveByteArrayJavaType/ByteArrayJavaType so the special case applies.

Example fix

// before: custom descriptor is not plural-aware
public class IntListJavaType extends AbstractJavaType<IntList> {
    public IntListJavaType() { super(IntList.class); }
}
// binding/literal rendering -> IllegalArgumentException: not a BasicPluralJavaType

// after: implement BasicPluralJavaType so the element type is derivable
public class IntListJavaType extends AbstractJavaType<IntList> implements BasicPluralJavaType<IntList> {
    public IntListJavaType() { super(IntList.class); }
    @Override public JavaType<Integer> getElementJavaType() { return IntegerJavaType.INSTANCE; }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Before registering/binding with an array JdbcType, check the descriptor
JavaType<?> jt = typeConfiguration.getJavaTypeRegistry().findDescriptor(MyList.class);
if (!(jt instanceof BasicPluralJavaType<?>)
        && !(jt instanceof ByteArrayJavaType)
        && !(jt instanceof PrimitiveByteArrayJavaType)) {
    throw new IllegalStateException(
        "ArrayJdbcType needs a BasicPluralJavaType descriptor; got " + jt.getClass().getName());
}

Type guard

static boolean supportsArrayJdbcType(JavaType<?> jt) {
    return jt instanceof BasicPluralJavaType<?>
        || jt instanceof ByteArrayJavaType
        || jt instanceof PrimitiveByteArrayJavaType;
}

Try / catch

try {
    return arrayJdbcType.getJdbcLiteralFormatter(domainJavaType);
} catch (IllegalArgumentException e) {
    if ("not a BasicPluralJavaType".equals(e.getMessage())) {
        // domain descriptor must implement BasicPluralJavaType and expose its element type
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling ArrayJdbcType.getJdbcLiteralFormatter(...) - reached when Hibernate inlines an array literal into HQL/criteria SQL - or getElementTypeName(...) - reached when creating a java.sql.Array during binding - with a domain JavaType that is neither byte[]/Byte[] nor a BasicPluralJavaType. Typically a custom JavaType for an array-like type that does not implement BasicPluralJavaType, or an array JdbcType mapped onto a scalar attribute.

Common situations: Custom JavaType implementations for array-backed domain types that forget to implement BasicPluralJavaType; @JdbcType naming an array type while the attribute is a scalar; dialect/Hibernate upgrades that made this previously lenient path throw.

Related errors


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