'Spring Batch - RepositoryItemReaderBuilder with RepositoryMethodReference throws Cannot subclass final class

I'm trying to use Spring Batch RepositoryItemReaderBuilder with RepositoryMethodReference as parameter to provide type-safe way to call repository methods.

unfortunately I can't make this work since this throws

java.lang.IllegalArgumentException: Cannot subclass final class com.sun.proxy.$Proxy100

as far as I understand, to make this work repository should not be final, but how can I do it?

    implementation 'org.springframework.batch:spring-batch-core:4.3.5'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.6.7'
@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    private String firstName;
    
    ...constructors, getters, setters
}

public interface CustomerRepository extends PagingAndSortingRepository<Customer, Long> {

}
@SpringBootApplication
public class AccessingDataJpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(AccessingDataJpaApplication.class);
    }

    @Bean
    public CommandLineRunner demo(CustomerRepository repository) {
        return (args) -> {
            
            Customer customer = new Customer();
            customer.setFirstName("John");
            repository.save(customer);

            RepositoryItemReaderBuilder.RepositoryMethodReference<CustomerRepository> repositoryMethodReference =
                    new RepositoryItemReaderBuilder.RepositoryMethodReference<>(repository);
            repositoryMethodReference.methodIs().findAll();

            RepositoryItemReader<Customer> reader = new RepositoryItemReaderBuilder<Customer>()
                    .repository(repositoryMethodReference)
                    .sorts(Collections.singletonMap("id", Sort.Direction.ASC))
                    .maxItemCount(5)
                    .name("bar").build();

            Customer read = reader.read();
        };
    }

}


Solution 1:[1]

Workaround for this is to create

public class PagingAndSortingRepositoryWrapper<T, ID> implements PagingAndSortingRepository<T, ID> {

    private PagingAndSortingRepository<T, ID> pagingAndSortingRepository;

    public PagingAndSortingRepositoryWrapper() {
    }

    public PagingAndSortingRepositoryWrapper(PagingAndSortingRepository<T, ID> pagingAndSortingRepository) {
        this.pagingAndSortingRepository = pagingAndSortingRepository;
    }

    @Override
    public Iterable<T> findAll(Sort sort) {
        return pagingAndSortingRepository.findAll(sort);
    }
    @Override
    public Page<T> findAll(Pageable pageable) {
        return pagingAndSortingRepository.findAll(pageable);
    }
    ...

then repositoryWrapper will not be final and we will be able to use RepositoryMethodReference

PagingAndSortingRepositoryWrapper<Customer, Long> repositoryWrapper = new PagingAndSortingRepositoryWrapper<>(customerRepository);

        RepositoryItemReaderBuilder.RepositoryMethodReference<PagingAndSortingRepositoryWrapper> repositoryMethodReference =
                new RepositoryItemReaderBuilder.RepositoryMethodReference<>(repositoryWrapper);
        repositoryMethodReference.methodIs().findAll();

        RepositoryItemReader<Customer> itemReader = new RepositoryItemReaderBuilder<Customer>()
                .repository(repositoryMethodReference)
                .name("bar")
                .sorts(Collections.singletonMap("id", Sort.Direction.ASC))
                .build();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 0001