spring-data-jpa icon indicating copy to clipboard operation
spring-data-jpa copied to clipboard

java.lang.IllegalStateException is thrown by invoking findBy method

Open quaff opened this issue 2 years ago • 4 comments

java.lang.IllegalStateException: No MethodInvocation found: Check that an AOP invocation is in progress, and that the CrudMethodMetadataPopulatingMethodInterceptor is upfront in the interceptor chain.
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.currentInvocation(CrudMethodMetadataPostProcessor.java:123)
	at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$ThreadBoundTargetSource.getTarget(CrudMethodMetadataPostProcessor.java:294)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:229)
	at jdk.proxy2/jdk.proxy2.$Proxy100.getLockModeType(Unknown Source)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.applyRepositoryMethodMetadata(SimpleJpaRepository.java:845)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:756)
	at org.springframework.data.jpa.repository.support.SimpleJpaRepository.lambda$doFindBy$2(SimpleJpaRepository.java:521)
	at org.springframework.data.jpa.repository.support.FetchableFluentQueryBySpecification.createSortedAndProjectedQuery(FetchableFluentQueryBySpecification.java:183)
	at org.springframework.data.jpa.repository.support.FetchableFluentQueryBySpecification.stream(FetchableFluentQueryBySpecification.java:166)
	at findby.FindByTests.findBy(FindByTests.java:30)
	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)

Here is test case:

package findby;

import java.util.function.Function;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.jpa.domain.AbstractPersistable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.test.context.ContextConfiguration;

import jakarta.persistence.Entity;

@DataJpaTest
@EnableJpaRepositories(basePackageClasses = FindByTests.TestEntityRepository.class, considerNestedRepositories = true)
@EntityScan(basePackageClasses = FindByTests.TestEntity.class)
@ContextConfiguration(classes = FindByTests.class)
class FindByTests {

	@Autowired
	TestEntityRepository repository;

	@Test
	void findBy() {
		// will throw java.lang.IllegalStateException: No MethodInvocation found
		repository.findBy((root, query, cb) -> null, Function.identity()).stream();
	}

	@Test
	void findByInDefaultMethod() {
		// works fine
		repository.findByInDefaultMethod();
	}

	interface TestEntityRepository extends JpaRepository<TestEntity, Long>, JpaSpecificationExecutor<TestEntity> {

		default Stream<TestEntity> findByInDefaultMethod() {
			return findBy((root, query, cb) -> null, Function.identity()).stream();
		}

	}

	@Entity
	static class TestEntity extends AbstractPersistable<Long> {

	}

}

with

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.1'
	id 'io.spring.dependency-management' version 'latest.release'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testRuntimeOnly 'com.h2database:h2'
}

tasks.named('test') {
	useJUnitPlatform()
}

quaff avatar Jan 03 '24 03:01 quaff

I also ecountered this using "org.springframework.boot:spring-boot-starter-data-mongodb"

pbeltechi avatar Jan 09 '24 11:01 pbeltechi

Thank you @quaff for reporting and adding the test snippet. The current behaviour, though unintuitive, is the expected one. The findBy method in this case exposes the FetchableFluentQuery outside the repository. So when the stream invocation happens the context is already gone.

We're considering to enhance the current flow so that we capture method metadata early and can provide it later on in this scenario.

Meanwhile you can try it this way repository.findBy((root, query, cb) -> null, FetchableFluentQuery::stream).

christophstrobl avatar Jan 09 '24 13:01 christophstrobl

Meanwhile you can try it this way repository.findBy((root, query, cb) -> null, FetchableFluentQuery::stream).

Thanks, it works, does the document mention such limitation?

quaff avatar Jan 10 '24 01:01 quaff

Unfortunately not, all samples are using one of the terminating (one, first, all,...) methods.

christophstrobl avatar Jan 10 '24 07:01 christophstrobl