How to fix java.lang.IllegalStateException when using spring-data-neo4j - spring-boot

I have a simple test project where checking spring-data-neo4j with spring boot version: 2.1.0.RELEASE (https://github.com/tomkasp/neo4j-playground/blob/master/src/main/java/com/athleticspot/neo4jplayground/domain/AthleteRepository.java)
spring-data-neo4j (version: 5.1.4.RELEASE) dependency is injected by spring-boot-starter-data-neo4j.
My goal was to create a repository method which fetches data with containing and ingnorecase functionalities. In order to do that I've created below method within repository:
public interface AthleteRepository extends CrudRepository<Athlete, Long> {
List<Athlete> findByNameContainingIgnoreCase(String name);
}
When I run above functions I'm getting:
java.lang.IllegalStateException: Unable to ignore case of java.lang.String types, the property 'name' must reference a String
at org.springframework.util.Assert.state(Assert.java:73) ~[spring-core-5.1.2.RELEASE.jar:5.1.2.RELEASE]
at org.springframework.data.neo4j.repository.query.filter.PropertyComparisonBuilder.applyCaseInsensitivityIfShouldIgnoreCase(PropertyComparisonBuilder.java:101) ~[spring-data-neo4j-5.1.2.RELEASE.jar:5.1.2.RELEASE]
Doesn't spring-data-neo4j support Containing and IgnoreCase together? Am I missing something?

At the moment it seems not possible because the referenced org.springframework.data.neo4j.repository.query.filter.PropertyComparisonBuilder seems to allow ignoring case only for "SIMPLE_PROERTY" (is, or equals). See method canIgnoreCase in same class:
private boolean canIgnoreCase(Part part) {
return part.getType() == SIMPLE_PROPERTY && String.class.equals(part.getProperty().getLeafType());
}
Fix is coming with spring 5.2 (Moore): https://jira.spring.io/browse/DATAGRAPH-1190

Related

Quarkus extension using a repository based on PanacheMongoRepository

I'm currently working on a Quarkus extension which is basically a filter that is using a PanacheMongoRepository. Here is a code snippet (this is in the runtime part of the extension) :
#Provider
#Priority(Priorities.AUTHORIZATION)
#AuthorizationSecured
public class AuthorizationFilter implements ContainerRequestFilter {
// Some injection here
#Inject
UserRepository userRepository;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
// Some business logic here...
UserEntity userEntity = userRepository.findByName(name);
// Some business logic here...
}
}
The repository :
#ApplicationScoped
public class UserRepository implements PanacheMongoRepository<UserEntity> {
public UserEntity findByName(String name) {
return find("some query...", name).firstResult();
}
}
When the repository is called, I get the following exception:
org.jboss.resteasy.spi.UnhandledException: java.lang.IllegalStateException: This method is normally automatically overridden in subclasses...
java.lang.IllegalStateException: This method is normally automatically overridden in subclasses\n\tat io.quarkus.mongodb.panache.common.runtime.MongoOperations.implementationInjectionMissing(MongoOperations.java:765)\n\tat io.quarkus.mongodb.panache.PanacheMongoRepositoryBase.find(PanacheMongoRepositoryBase.java:119)
The processor
class AuthorizeProcessor {
private static final String FEATURE = "authorize";
#BuildStep
FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE);
}
#BuildStep(onlyIf = IsAuthorizeEnabled.class)
void registerAuthorizeFilter(
BuildProducer<AdditionalBeanBuildItem> additionalBeanProducer,
BuildProducer<ResteasyJaxrsProviderBuildItem> resteasyJaxrsProviderProducer
) {
additionalBeanProducer.produce(new AdditionalBeanBuildItem(UserRepository.class));
additionalBeanProducer.produce(new AdditionalBeanBuildItem(AuthorizationFilter.class));
resteasyJaxrsProviderProducer.produce(new ResteasyJaxrsProviderBuildItem(AuthorizationFilter.class.getName()));
}
}
Any idea ?
Thanks for your help :)
MongoDB with Panache (and the same for Hibernate with Panache) uses bytecode enhancement at build time. When this enhancement didn't occurs it leads to the exception you mentionned at runtime: java.lang.IllegalStateException: This method is normally automatically overridden in subclasses
It can occurs only when the repository or entity is not in the Jandex index. Jandex is used to index all the code of your application to avoid using reflection and classpath scanning to discover classes. If your entity / repository is not in the index this means it's not part of your application as we automatically index the classes of your application, so it must be inside an external JAR.
Usually, this is solved by adding the Jandex plugin to index the code of the external JAR (in fact there is multiple way to do this, see How to Generate a Jandex Index).
An extension suffer from the same issue as extensions are not indexed by default. But from an extension you can index the needed classes via a build step wich is more easy and avoid polluting the index with classes that are not needed.
This can be done by generating a new AdditionalIndexedClassesBuildItem(UserRepository.class.getName()) inside a build step.

Manually configure Jackson Module for Spring WebTestClient

Prerequisites
Spring Boot: 2.0.3.RELEASE
Spring REST Docs: 2.0.1.RELEASE
Spring Auto REST Docs: 2.0.2-SNAPSHOT - Which is a local build of the current master.
The GeoJsonModule is configured in the Main Application:
import org.springframework.data.mongodb.core.geo.GeoJsonModule;
#SpringBootApplication
public class MyApplication {
[...]
#Bean
public GeoJsonModule registerGeoJsonModule() {
return new GeoJsonModule();
}
[...]
}
I am using Spring Auto REST Docs and therefore cannot use the #AutoConfigure-Annotation (Or atleast I don't know how).
Current Implementation
I configure the WebTestClient like so:
WebTestClient
.bindToApplicationContext(context)
.configureClient()
.filter(
WebTestClientRestDocumentation
.documentationConfiguration(restDocumentation)
.snippets()
.withDefaults(
WebTestClientInitializer.prepareSnippets(context),
CliDocumentation.curlRequest(),
HttpDocumentation.httpRequest(),
HttpDocumentation.httpResponse(),
AutoDocumentation.requestFields(),
AutoDocumentation.responseFields(),
AutoDocumentation.pathParameters(),
AutoDocumentation.requestParameters(),
AutoDocumentation.description(),
AutoDocumentation.methodAndPath(),
AutoDocumentation.section()
)
)
.build();
However when using the WebTestClient like this I get the following error:
org.springframework.core.codec.CodecException: Type definition error: [simple type, class org.springframework.data.mongodb.core.geo.GeoJsonPoint]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `org.springframework.data.mongodb.core.geo.GeoJsonPoint` (no Creators, like default construct, exist): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: com.example.MyClass["location"])
So I looked around and found, that the WebTestClientAutoConfiguration and SpringBootWebTestClientBuilderCustomizer are additionally customizing Codecs.
Changing the initialization to include the customizers fixes the deserialization issue and it works.
Collection<CodecCustomizer> customizers = context.getBeansOfType(CodecCustomizer.class).values();
SpringBootWebTestClientBuilderCustomizer builderCustomizer = new SpringBootWebTestClientBuilderCustomizer(customizers);
builderCustomizer.customize(builder);
builder.build();
However I do not know if this is the correct way of configuring the WebTestClient and if it works entirely or if something is still broken I just did not discover yet.
Somehow I don't think that this is the intended way and I would like to know how the WebTestClient can be configured properly.

SpringBoot scanBasePackages not working in multi module project

I have the following maven project structure:
eu.arrowhead
common
repository
-AJpaRepository.class
orchestrator
controller
-AController.class
OrchestratorApplication
other_modules...
Where two of the modules are common, and orchestrator. Common is a dependency for the Orchestrator module. The JpaRepositoryClass is annotated with #Repository.
In the controller class I use the constructor autowiring to get a copy of the repository:
private final AJpaRepository serviceRepo;
#Autowired
public AController(AJpaRepository serviceRepo){
this.serviceRepo = serviceRepo;
}
And finally, in the Application class, I use scanBasePackages, to pick up the components from the common module:
#SpringBootApplication(scanBasePackages = "eu.arrowhead")
public class OrchestratorApplication {
public static void main(String[] args) {
SpringApplication.run(OrchestratorApplication.class, args);
}
}
When I start the application, I get:
Description:
Parameter 0 of constructor in eu.arrowhead.orchestrator.controller.ArrowheadServiceController required a bean of type 'eu.arrowhead.common.repository.ArrowheadServiceRepo' that could not be found.
Action:
Consider defining a bean of type 'eu.arrowhead.common.repository.ArrowheadServiceRepo' in your configuration.
If I use scanBasePackages = {"eu.arrowhead.common"} then the application starts without an error, but I can not reach the endpoint in my controller class (getting the default 404 error). If I write scanBasePackages = {"eu.arrowhead.common", "eu.arrowhead.orchestrator"} it's the same as if only "eu.arrowhead" is there, I get the same error at startup.
Is this how this supposed to work? I highly doubt it.
Depencendies:
Common module: starter-data-jpa, starter-json, mysql-connector-java, hibernate-validator
Orch module: starter-web, the common module.
I also tried using #ComponentScan, but had the same result. What is the problem? Thanks.
You are missing #EnableJpaRepositories("eu.arrowhead") annotation to enable Spring Data JPA repository scanning.

Spring Boot and Spring Data Couchbase N1QL auto-generated query

I am trying to create a simple prototype using Spring Boot and Spring Data Couchbase projects. I have been stymied so far by trying to use the Spring-Data’s query derivation mechanism to build a N1QL query from the method name.
This is what I have for my repository interface definition and the problem is in the findBy... line.
public interface MetricsRepository extends CrudRepository<Single, String> {
Single save(Single entity);
Single findOne(String id);
List<Single> findByServiceID(long serviceId);
}
If I exclude that method definition, the application starts without a problem. If I include it, the repository bean fails to be created with due to the following error:
Caused by: java.lang.AbstractMethodError: org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory$CouchbaseQueryLookupStrategy.resolveQuery(Ljava/lang/reflect/Method;Lorg/springframework/data/repository/core/RepositoryMetadata;Lorg/springframework/data/repository/core/NamedQueries;)Lorg/springframework/data/repository/query/RepositoryQuery;
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:416)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:206)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:251)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:237)
at org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactoryBean.afterPropertiesSet(CouchbaseRepositoryFactoryBean.java:96)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1637)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1574)
... 36 more
If I specify a #Query (for example: #Query("#{#n1ql.selectEntity} WHERE role = $1") I get the same error.
My entity definition:
import com.couchbase.client.java.repository.annotation.Id;
import com.couchbase.client.java.repository.annotation.Field;
import org.springframework.data.couchbase.core.mapping.Document;
#Document
public class Single {
#Id private final String eventID;
#Field private final long serviceID;
#Field private final long metric;
#Field private final long timestamp;
#Field private final long previous;
public Single(String eventID, long serviceID, long metric, long timestamp, long previous) {
this.eventID = eventID;
this.serviceID = serviceID;
this.metric = metric;
this.timestamp = timestamp;
this.previous = previous;
}
public String getEventID() { return eventID; }
public long getServiceID() { return serviceID; }
public long getMetric() { return metric; }
public long getTimestamp() { return timestamp; }
public long getPrevious() { return previous; }
}
I'm using the repository via #Autowired annotation in a REST controller.
I have a #Configuration and #EnableCouchbaseRepositories config class #Import-ed into the #SpringBootApplication. I have a Couchbase Server 4.0.0 community version installed on my test instance, and if the n1ql query is not there, I can connect and store and retrieve entities.
My dependencies in gradle:
dependencies {
compile("org.springframework.data:spring-data-couchbase:2.1.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-web")
compile("org.springframework.boot:spring-boot-starter-actuator")
testCompile("org.springframework.boot:spring-boot-starter-test")
testCompile("junit:junit")
}
So I did a bit of dependency detective work:
Spring Boot 1.3.3 refers to the Spring Data BOM in version 1.7.4 (Gosling SR4)
That version of the Spring Data BOM brings in spring data-couchbase 1.4.4. That's what you'll get unless you force the version, like you did.
But what is the root of your problem is that it also brings in spring-data-commons 1.11.4 (of course, as BOM are made to bundle a coherent set of versions)
So you end up forcing the version of just the Spring Data Couchbase dependency, while relying on the BOM to choose the Spring Data Commons, resulting on incompatible artifacts.
Here is a bit of a bad news: if you want Spring Data Couchbase 2.x (the new "generation"), it is only officially supported in Spring Data Hopper. And Hopper is only officially supported in Spring Boot 1.4.0, which ** is currently in MILESTONE 2.
If you only want to start playing with Spring Data Couchbase 2.x, maybe using Spring Boot 1.4.0.M2 is acceptable?
If you don't really care about the 2.x version, you should simply remove the version in your Gradle configuration and it should work.
DANGER ZONE: If you absolutely want to do some Spring Data Couchbase 2.1.1 inside of Spring Boot 1.3.3, then note that anything could break if a dependency clashes. That said, if you only use the Couchbase data store, maybe you'll be ok. The way to do that is complicated with Gradle as you need a plugin to import the BOM. Have a look at this gist.
I've updated the project site to note that the BOM should generally be favored over forcing the version. The quick start there is for standalone projects.
I had the same problem (java.lang.AbstractMethodError) with Spring Data Couchbase 2.1.1. But in this case I was using other dependencies in the project, using a different required version of Spring Commons, and they were in conflict. Try to use a different version, maybe 2.1.0.

MappingInstantiationException upon retrieving multi-dim array of doubles from mongo in spring

I am building a spring MVC app with MongoDB. How can I read matrices in spring from mongo? I have a model which persists to mongo just fine using the MongoTemplate class:
Matrix m = new Matrix();
m.setId(UUID.randomUUID().toString());
m.setValues(values);
mongoTemplate.insert(m, "matrix");
The above code works just fine. Values is a double[][] and it is persisted. I am using an extension of the MongoRepository class to make a findAll() call for a list of matrices.
public interface MatrixRepository extends MongoRepository<Matrix, String> {
Matrix findById(String id);
}
And in my service class:
public List<Matrix> readAll() {
return matrixRepository.findAll();
}
This calling this causes the following stack trace:
org.springframework.data.mapping.model.MappingInstantiationException: Could not instantiate bean class [java.lang.Double]: No default constructor found; nested exception is java.lang.NoSuchMethodException: java.lang.Double.<init>()
org.springframework.data.mapping.model.BeanWrapper.<init>(BeanWrapper.java:105)
org.springframework.data.mapping.model.BeanWrapper.create(BeanWrapper.java:73)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:239)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:187)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.readCollectionOrArray(MappingMongoConverter.java:736)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.getValueInternal(MappingMongoConverter.java:695)
org.springframework.data.mongodb.core.convert.MappingMongoConverter$2.doWithPersistentProperty(MappingMongoConverter.java:252)
org.springframework.data.mongodb.core.convert.MappingMongoConverter$2.doWithPersistentProperty(MappingMongoConverter.java:242)
org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:173)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:242)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:187)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:151)
org.springframework.data.mongodb.core.convert.MappingMongoConverter.read(MappingMongoConverter.java:73)
org.springframework.data.mongodb.core.MongoTemplate$ReadDbObjectCallback.doWith(MongoTemplate.java:1693)
org.springframework.data.mongodb.core.MongoTemplate.executeFindMultiInternal(MongoTemplate.java:1444)
org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1259)
org.springframework.data.mongodb.core.MongoTemplate.doFind(MongoTemplate.java:1248)
org.springframework.data.mongodb.core.MongoTemplate.find(MongoTemplate.java:471)
org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:255)
org.springframework.data.mongodb.repository.support.SimpleMongoRepository.findAll(SimpleMongoRepository.java:192)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
It happens if your monogo entity class has Double or double fields in spring data mongodb 1.0.0.M5 environment.
You can fix this by replacing your spring data mongodb with a newer version 1.3.3 RELEASE in your pom.xml

Resources