spring-projects / spring-data-couchbase

Provides support to increase developer productivity in Java when using Couchbase. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.
https://spring.io/projects/spring-data-couchbase
Apache License 2.0
274 stars 190 forks source link

Runtime error when calling DeleteAllById(Iterable<T> list) if the repository extends DynamicProxyable #1939

Closed zandrei closed 4 months ago

zandrei commented 4 months ago

In the case of an application repository extending ReactiveCouchbaseRepository and DynamicProxyable interfaces, calling deleteAllById(Iterable<? extends ID> ids) method on the application repository will result in the following runtime exception:

java.lang.reflect.UndeclaredThrowableException at jdk.proxy2/jdk.proxy2.$Proxy126.deleteAllById(Unknown Source) at org.springframework.data.couchbase.repository.query.ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests.myTest1(ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests.java:128) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) Caused by: java.lang.NoSuchMethodException: No such method: deleteAllById at org.springframework.data.couchbase.repository.support.FindMethod.internalFind(FindMethod.java:84) at org.springframework.data.couchbase.repository.support.FindMethod.findMethod(FindMethod.java:33) at org.springframework.data.couchbase.repository.support.DynamicInvocationHandler.invoke(DynamicInvocationHandler.java:118) ... 5 more

Test class: ReactiveCouchbaseRepositoryQueryCollectionIntegrationTests Test method to reproduce:

@Test
    public void deleteAllByIdDynamicProxyableTest() {

        ReactiveAirportRepository ar = reactiveAirportRepository.withScope(scopeName).withCollection(collectionName);
        Airport vie = new Airport("airports::vie", "vie", "loww");
        try {
            Airport saved = ar.save(vie).block();
            ar.deleteAllById(List.of(saved.getId())).block();
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            ar.delete(vie).block();
        }
    }

The problem does not exhibit if the application repository extends only ReactiveCouchbaseRepository.

Test class: ReactiveCouchbaseRepositoryKeyValueIntegrationTests

Test method that passes:

@Test
    @IgnoreWhen(clusterTypes = ClusterType.MOCKED)
    void deleteAllByIdNoDynamicProxyableTest() {
        // the User class has a version.
        User user = new User(UUID.randomUUID().toString(), "f", "l");
        // save the document - we don't care how on this call
        userRepository.save(user).block();
        // Now set the version to 0, it should attempt an insert and fail.
        long saveVersion = user.getVersion();
        user.setVersion(0);
        assertThrows(DuplicateKeyException.class, () -> userRepository.save(user).block());
        user.setVersion(saveVersion + 1);
        assertThrows(OptimisticLockingFailureException.class, () -> userRepository.save(user).block());
        userRepository.delete(user);

        // Airline does not have a version
        Airline airline = new Airline(UUID.randomUUID().toString(), "MyAirline", null);
        // save the document - we don't care how on this call
        reactiveAirlineRepository.save(airline).block();
        reactiveAirlineRepository.save(airline).block(); // If it was an insert it would fail. Can't tell if an upsert or
        // cleanup.
        reactiveAirlineRepository.deleteAllById(List.of(airline.getId())).block();
    }

Tested with spring data couchbase 5.2.3, couchbase client 2.4.11