Spring Boot 3.3+ JPA Query Method Naming Breaking Changes

When upgrading to Spring Boot 3.3+, you might encounter this error:

org.springframework.data.repository.query.QueryCreationException: 
Could not create query for public abstract java.util.List com.example.repository.UserRepository.findByEmailAddress(java.lang.String emailAddress); 
Reason: Failed to create query for method public abstract java.util.List com.example.repository.UserRepository.findByEmailAddress(java.lang.String emailAddress); 
Invalid derived query! No property 'emailAddress' found for type 'User' (Did you mean 'email'?)

This error occurs because Spring Boot 3.3+ introduced stricter validation for JPA query method names. What causes this and how can it be fixed?

Solution

The issue is caused by Spring Boot 3.3+ introducing stricter validation for JPA query method names. The framework now validates that property names exist in the entity and method names follow exact conventions.

Fix the method name to match the actual entity property:

// Before (broken)
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByEmailAddress(String emailAddress); // 'emailAddress' doesn't exist
}

// After (fixed)
public interface UserRepository extends JpaRepository<User, Long> {
    List<User> findByEmail(String email); // 'email' property exists
}

Or use a custom query if you need to keep the method name:

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE u.email = :emailAddress")
    List<User> findByEmailAddress(@Param("emailAddress") String emailAddress);
}

Ensure your entity properties match:

@Entity
public class User {
    private String email; // Property name matches method
    
    // Getters and setters
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

This stricter validation actually helps catch bugs early and makes your code more maintainable.

Alternative #1

If you have many repository methods to fix, you can use Spring Data JPA's property path resolution to create more flexible queries. Instead of renaming all your methods, you can use @Query with property paths:

public interface UserRepository extends JpaRepository<User, Long> {
    // Use property path if the entity has nested properties
    @Query("SELECT u FROM User u WHERE u.profile.email = :emailAddress")
    List<User> findByEmailAddress(@Param("emailAddress") String emailAddress);
    
    // Or use SpEL expressions for complex queries
    @Query("SELECT u FROM User u WHERE u.email LIKE %:#{#emailAddress}%")
    List<User> findByEmailAddressContaining(@Param("emailAddress") String emailAddress);
}

This approach is useful when you have complex entity relationships or need to maintain backward compatibility with existing method names.

You can also use method name aliases in your service layer:

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public List<User> findByEmailAddress(String emailAddress) {
        // Call the fixed repository method
        return userRepository.findByEmail(emailAddress);
    }
}

This way, your service API remains unchanged while fixing the underlying repository issue.

Alternative #2

For enterprise applications with many repositories, consider using Spring Data JPA's specification pattern to avoid method naming issues altogether. This approach gives you more flexibility and eliminates dependency on naming conventions.

Create a base repository interface:

public interface BaseRepository<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
    // Common methods that don't rely on property names
}

Then use specifications for complex queries:

public interface UserRepository extends BaseRepository<User, Long> {
    // Simple methods that work with property names
    List<User> findByEmail(String email);
    
    // Complex queries using specifications
    default List<User> findByEmailAddress(String emailAddress) {
        return findAll(Specifications.where(
            (root, query, cb) -> cb.equal(root.get("email"), emailAddress)
        ));
    }
}

The specification pattern offers several advantages. You're no longer constrained by method naming conventions, making your code more flexible and maintainable. It's particularly useful for complex business logic and makes testing and debugging easier.

When implementing this approach, start by keeping existing method names in your service layer, then gradually update repository implementations to use specifications. This migration strategy works well for legacy code that you can't easily refactor or when you need dynamic query building capabilities.

Last modified: August 19, 2025
Stay in the loop
Subscribe to our newsletter to get the latest articles delivered to your inbox