Skip to content

rwitzel/CouchRepository

Repository files navigation

Some parts of this documentation require knowledge about CouchDB IDs, revisions, and views.

Travis build status Coveralls coverage status Apache 2 Maven Central

When should I use CouchRepository?

There are two main use cases.

A. You choose CouchRepository when

  • you are going to use Spring Data for a new project and
  • you do not want to decide about the eventual database solution yet.

In this case, you make your new application configurable so that one can easily switch between different persistence solutions (SQL, MongoDB, CouchDB).

B. You choose CouchRepository when

  • you want to stick with the familiar Spring Data framework, and
  • you are going to work with an existing CouchDB database, and
  • you need only basic CRUD operations and simple view queries in the first place.

As soon as you want to use more sophisticated operations on the CouchDB, you will directly use the underlying Java CouchDB drivers, namely LightCouch or Ektorp.

How do I use CouchRepository?

You may want to checkout the source code and have a look at the tests to find working examples for both the Ektorp and LightCouch drivers.

Prerequisites

CouchRepository requires Java 8 or higher.

Basics

Here is a short example that assumes you use Ektorp as CouchDB driver. The code for LightCouch is almost the same.

First of all you add the needed dependencies to your project. The dependencies for Ektorp must be added explicitly because you can choose between Ektorp and LightCouch.

compile 'com.github.rwitzel:couchrepository-core:1.0.0'
compile 'org.ektorp:org.ektorp:1.4.2'
compile 'org.ektorp:org.ektorp.spring:1.4.2'

Have a look a the CouchRepository Example to get a full list of the needed dependencies.

Then you set up a CouchDbConnector as described by the Ektorp documentation.

CouchDbConnector db = ...

Then you create a repository for each type of entities. For an entity type like Product you write

CouchDbCrudRepository<Product,String> productRepository = new EktorpCrudRepository(Product.class, db);

The type CouchDbCrudRepository extends Spring Data's CrudRepository so that you are able to query CouchDB views immediately by using the find method.

One thing is still missing. The repository requires a design document (by default named _design/Product) that provides a view named by_id. This view must provide ID and revision for each entity of the given type. The reduce function must be _count. The design document may look like this.

{
  "_id" : "_design/Product",
  "language" : "javascript",
  "views" : {
    "by_id" : {
      "map" : "function(doc) { if(doc.type == 'Product') {emit(doc._id, { _id : doc._id, _rev: doc._rev } )} }",
      "reduce" : "_count"
    }
} 

One way to load the design document into the database is DocumentLoader but you can use any other mean, of course. You can use the repository as soon the design document is available in the database.

DocumentLoader loader = new DocumentLoader(new EktorpCrudRepository(Map.class, db));
loader.loadJson(getClass().getResourceAsStream("../Product.json")); // design document Product.json is taken from the classpath

In case you prefer YAML documents, you can use the method loadYaml instead of loadJson. In YAML documents you don't have to care about the formatting of the contained Javascript functions.

Finally, your Java-based Spring configuration might look like this.

@Configuration
public class CouchDbRepositoriesConfiguration {

    @Bean
    public StdCouchDbConnector connector() throws Exception { ... }

    @Bean
    public CouchDbCrudRepository<Product,String> productRepository(CouchDbConnector db) { ... }

    @Bean @Lazy(false)
    public String initializeDatabaseAndDesignDocuments(CouchDbConnector db) { ... }
}

Automatic implementation of finder methods

If you want to use the automatic implementation of finder methods, you create an interface that contains the desired methods.

public interface ProductRepository extends CouchDbCrudRepository<Product, String> {

    List<Comment> findByComment(Object[] key, ViewParams viewParams);

    ViewResult findByComment(Object[] key, Boolean descending, ViewParams viewParams, Class<?> valueType);
}

The method parameter viewParams allow you to specify arbitrary parameters for the view query. But usually you will add more parameters to the method signature because they cover your main use cases, here: key. Parameters set in viewParams do not override parameters that are contained in the method signature, i.e the key in viewParams is ignored. Attention! Please read more about method parameter name detection in section Q&A.

To actually use the methods, your design document must contain a view findByComment that is appropriate for your methods.

{
  "_id" : "_design/Product",
  "language" : "javascript",
  "views" : {
    ...,
    "findByComment" : {
      "map" : "... your custom implementation ...",
      "reduce" : "... your custom implementation ..."
    }
  }
}

Finally, you create a proxy that replaces the existing repository. The proxy wraps the original repository and adds the custom finder methods.

CouchDbCrudRepositoryFactory factory = new CouchDbCrudRepositoryFactory();
productRepository = factory.createRepository(productRepository, null, ProductRepository.class);

Custom implementations

In some cases you want to provide custom implementations of finder methods. For our example we create an interface called ProductRepositoryCustom.

public interface ProductRepositoryCustom {
    List<Product> findByComment(Object[] key, Boolean descending);
}

Then we create an implementation of this interface.

public class ProductRepositoryCustomImpl implements ProductRepositoryCustom {

    private CouchDbCrudRepository<Product, String> repository;

    public ProductRepositoryCustomImpl(CouchDbCrudRepository<Product, String> repository) {
        super(); 
        this.repository = repository;
    }

    public List<Product> findByComment(Object[] key, Boolean descending) {

        ViewParams params = new ViewParams();
        params.setReduce(false);
        params.setIncludeDocs(true);
        params.setReturnType("doc");
        params.setDocumentType(Product.class);
        params.setValueType(Object.class);
        params.setKeyType(Object.class);
        params.setView("findByComment");

        params.setKey(key);
        params.setDescending(descending);

        return repository.find(params);
    }
}

Now the creation of the proxy is a bit different.

ProductRepositoryCustom customRepository = new ProductRepositoryCustomImpl(productRepository);

productRepository = factory.createRepository(productRepository, customRepository, ProductRepository.class);

How to develop

First start a local CouchDB server (check http://127.0.0.1:5984/_utils/).

Add credentials in LightCouchTestConfiguration and EktorpTestConfiguration as appropriate:

properties.setUsername("admin");
properties.setPassword("admin");

Then run the tests.

mvn clean install

More Questions and Answers

Q. Which CouchDB drivers are supported?

A. Based on the activity derived from the commit history, this project tried to use the following Java drivers and Scala drivers:

  • Ektorp. A lot of commits in 2014.
  • LightCouch. A lot of commits in 2014.
  • Sohva. A lot of commits in 2014.

At the moment Sohva does not provide an API that can be easily used by Java applications. Therefore, Sohva is not used in this project but Ektorp and LightCouch adapters are implemented.

Q. The exception com.thoughtworks.paranamer.ParameterNamesNotFoundException is thrown. What can I do?

A. Paranamer is used to identify the names of method parameters. Go to the project page to get more information about the exception.

For Java 7 you could try the AnnotationParanamer. In this case you have to add JSR-330 to your classpath (javax.inject:javax.inject:1).

For Java 8 try Java8Paranamer. Java8Paranamer is a single class you can copy to your source code. Be aware that even for JDK 8 you have to activate a compiler option: -parameters.

Then configure a custom CouchDbCrudRepositoryFactory with your chosen paranamer.

factory = new CouchDbCrudRepositoryFactory(new ViewParamsMerger(.. your paranamer here...));

Q. Do I have to modify my entity classes to make them compatible with CouchDB?

Both Ektorp and LightCouch use JSON serialization frameworks like Jackson and Gson in order to save and load entities. Therefore, most probably you have to add annotations to your entity classes.

Additionally, in many cases you will have to add a property for the CouchDB revision. If the IDs of your entities are not of type String, then you have to add a property for the CouchDB ID.

If your entity classes store ID and revision not in properties with standard names like _id, id resp. _rev, rev, revision, then use EntityInformation to grant CouchRepository access to the ID and the revision.

Q. Can I use IDs that are of other type than String, like Integer, a custom class etc.?

A. Yes, but natively CouchDB (up to 1.6.1) supports only IDs of type String. Therefore, you have to define a mapping between the CouchDB ID and your domain-specific ID. You will find an example among the unit tests: Look for the class Exotic in the source code to find a working example.

Q. Are you going to support Spring Data's API for paging and sorting?

A. No, there is no plan to support PagingAndSortingRepository.

Q. Are you going to support XML-based or annotation-based definitions of Spring Data repositories?

A. No, there is no plan to do this because the benefit of the declarative approach does not look very big at the moment. A simple Java-based Spring configuration is sufficient.

Q. How is the performance of CouchRepository?

A. The performance is determined by the performance of the underlying CouchDB drivers and the design of CouchDB at all. Thus, a method like deleteAll has to fetch all document IDs from the database to delete the documents.

Q. How does the future of CouchRepository look like?

A. CouchRepository was build for a single purpose, and it already serves this purpose. Thus, at the moment there are no plans to add more features. But feel free to suggest improvements or to send in patches.

About

Spring Data repositories for CouchDB

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages