Search code examples
springspring-bootspring-datacouchbasespring-data-couchbase

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")
}

Solution

  • 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.