hibernate/hibernate-orm · error · PropertyNotFoundException

Could not locate setter method for property '%s' of class '%

Error message

Could not locate setter method for property '%s' of class '%s'

What it means

Property access needs a writer as well as a reader. ReflectHelper.findSetterMethod searches the class hierarchy and interfaces for a public setFoo(...) whose single parameter equals the property type; when no method matches both the name and the parameter type, it throws PropertyNotFoundException. A setter taking a different type than the property is rejected exactly like a missing one.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/internal/util/ReflectHelper.java:736

		Method potentialSetter = null;

		for ( Method method : theClass.getDeclaredMethods() ) {
			final String methodName = method.getName();
			if ( method.getParameterCount() == 1 && methodName.equals( setterName ) ) {
				potentialSetter = method;
				if ( propertyType == null || method.getParameterTypes()[0].equals( propertyType ) ) {
					break;
				}
			}
		}

		return potentialSetter;
	}

	public static Method findSetterMethod(final Class<?> containerClass, final String propertyName, final Class<?> propertyType) {
		final Method setter = setterMethodOrNull( containerClass, propertyName, propertyType );
		if ( setter == null ) {
			throw new PropertyNotFoundException(
					String.format(
							Locale.ROOT,
							"Could not locate setter method for property '%s' of class '%s'",
							propertyName,
							containerClass.getName()
					)
			);
		}
		return setter;
	}

	private static Method setterOrNull(Class<?>[] interfaces, String propertyName, Class<?> propertyType, String likelyMethodName) {
		Method setter = null;
		for ( int i = 0; setter == null && i < interfaces.length; ++i ) {
			final var anInterface = interfaces[i];
			if ( !shouldSkipInterfaceCheck( anInterface ) ) {
				setter = setterOrNull( anInterface, propertyName, propertyType, likelyMethodName );
				if ( setter == null ) {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add a public setter setFoo(T) whose parameter type exactly matches the property/field type.
  2. Annotate the class or property with @Access(AccessType.FIELD) so Hibernate writes the field directly.
  3. For immutable values, map them as records/@Embeddable populated through the constructor instead of setters.
  4. If a setter already exists, make it public and align its parameter type with the mapped property type.

Example fix

// before: default property access, no setter
@Entity
public class Account {
    @Id private Long id;
    private BigDecimal balance; // no setBalance -> PropertyNotFoundException
}

// after: field access, no setter required
@Entity
@Access(AccessType.FIELD)
public class Account {
    @Id private Long id;
    private BigDecimal balance;
}
Defensive patterns

Strategy: validation

Validate before calling

static void verifySetters(Class<?> entityClass, List<String> mappedPropertyNames) {
    try {
        java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(entityClass);
        java.util.Set<String> writable = new java.util.HashSet<>();
        for (java.beans.PropertyDescriptor pd : info.getPropertyDescriptors()) {
            if (pd.getWriteMethod() != null) writable.add(pd.getName());
        }
        for (String name : mappedPropertyNames) {
            if (!writable.contains(name)) {
                throw new IllegalStateException("No setter for mapped property '" + name + "' on " + entityClass.getName());
            }
        }
    } catch (java.beans.IntrospectionException e) {
        throw new IllegalStateException("Cannot introspect " + entityClass.getName(), e);
    }
}

Type guard

static boolean hasSetter(Class<?> clazz, String property) {
    try {
        return new java.beans.PropertyDescriptor(property, clazz).getWriteMethod() != null;
    } catch (java.beans.IntrospectionException e) {
        return false;
    }
}

Try / catch

try {
    SessionFactory sf = metadata.buildSessionFactory();
} catch (org.hibernate.PropertyNotFoundException e) {
    // message names the property and class; add the setter or switch to @Access(AccessType.FIELD)
}

Prevention

When it happens

Trigger: SessionFactory bootstrap or runtime property writes on a property-access mapping where the mapped property has no public setter, or where the only setter's parameter type differs from the property/field type. Direct calls to ReflectHelper.findSetterMethod with such a class/property pair hit the same path.

Common situations: Immutable or builder-style classes without setters; setter parameter type widened or narrowed during a refactor; package-private setters; @Embeddable components lacking setters; switching a class from field access to default property access without adding writers.

Related errors


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