One of the biggest barriers for anyone who wants to start using new technologies is usually the learning curve. Often while starting a new project, we end up choosing to use what we already know to avoid any friction right at the beginning of the project.

I have spent most of my career working as a Java developer, and in the last few years I fell in love with the JPA + Spring-Boot + Lombok + Spring Data combination, but the one thing that still annoyed me was mapping relationships.

JPA is known for loading unnecessary data from the database, and over time you are forced to revisit some of your entities to change a few relationships from EAGER to LAZY. It can improve significantly your performance as you will avoid a lot of unnecessary JOINS but it does not come for free. You will be required to do a lot of refactoring to load those new lazy objects whenever they are required.

This common pattern always bothered me and I was really happy when I found that Couchbase has a connector for Spring Data (full doc here). It is simply the best part of two worlds, I can program like I would in a relational database but still leveraging all the speed of Couchbase and the power of N1QL. Let’s see how to setup a simple project:

 

Setting-up Couchbase with Spring Boot and Spring Data

 

Prerequisites:

  • I will assume that you already have Couchbase installed, if you don’t, please download it here
  • I am also using Lombok, so you might need to install Lombok’s plugin on your IDE: Eclipse and IntelliJ IDEA

 

First, you can clone my project:

or simply go to Spring-Boot Initialzr and add Couchbase and Lombok as dependencies:

Note: Lombok is not a required dependency, but it helps to reduce significantly your code base.

 

Now, let’s define your bucket configuration in the application.properties file:

And that’s it! You are already able to start up your project using:

 

Mapping an Entity

So far, our project does not do anything. Let’s create and map our first entity:

 

    • @Document: Couchbase’s annotation which defines an entity, similar to @Entity in JPA. Couchbase will automatically add a property called _class in the document to use it as the document type.
    • @Data:  Lombok’s annotation, auto-generate getters and setters
    • @AllArgsConstructor: Lombok’s annotation, auto-generate a constructor using all fields of the class, this constructor is used in our tests.
    • @NoArgsConstructor: Lombok’s annotation, auto-generate a constructor with no args (required by Spring Data)
    • @EqualsAndHashCode: Lombok’s annotation, auto-generate equals and hashcode methods, also used in our tests.
    • @NotNull: Yes! You can use javax.validation with Couchbase.
    • @Id: The document’s key
    • @Field:  Couchbase’s annotations, similar to @Column

 

Mapping entities in Couchbase is really simple and straightforward, the biggest difference here is the @Field entity which is used in 3 different ways:

  • Simple property: In cases like id, name and companyId, the @Field acts pretty much like the @Column in JPA.  It will result in a simple property in the document:

  • Arrays: In the phoneNumbers’s case it will result in an array inside the document:

  • Entities: Finally, in the areas’s case, @Field acts like a @ManyToOne relationship, the main difference is that you are not required to map anything in the Area entity:

 

 

Repositories

Your repositories will look very similar to standard Spring Data repositories but with a few extra annotations:

 

  • @N1qlPrimaryIndexed: This annotation makes sure that the bucket associated with the current repository will have a N1QL primary index
  • @ViewIndexed:  This annotation lets you define the name of the design document and View name as well as a custom map and reduce function.

In the repository above, we are extending CouchbasePagingAndSortingRepository, which allows you to paginate your queries by simply adding a Pageable param at the end of your method definition

As it is essentially a repository, you can leverage all Spring Data keywords like FindBy, Between, IsGreaterThan, Like, Exists, etc. So, you can start using Couchbase with almost no previous knowledge and still be very productive.

As you might have noticed, you can create full N1QL queries but with a few syntax-sugars:

  • #(#n1ql.bucket):  Use this syntax avoids hard-coding your bucket name in your query
  • #{#n1ql.selectEntity}: syntax-sugar to SELECT * FROM #(#n1ql.bucket):  
  • #{#n1ql.filter}: syntax-sugar to filter the document by type, technically it means class = ‘myPackage.MyClassName’ (_class is the attribute automatically added in the document to define its type when you are working with Couchbase on Spring Data )
  • #{#n1ql.fields} will be replaced by the list of fields (eg. for a SELECT clause) necessary to reconstruct the entity.
  • #{#n1ql.delete} will be replaced by the delete from statement.
  • #{#n1ql.returning} will be replaced by returning clause needed for reconstructing entity.

To demonstrate some of the cool capabilities of N1QL, let’s go a little deeper in two methods of our repository: findByPhoneNumber and findByCompanyAndAreaId:

 

findByPhoneNumber

In the case above we are simply searching for buildings by telephone numbers. In a relational world, you would normally need 2 tables to accomplish nearly the same thing. With Couchbase we can store everything in a single document, which makes loading a “building” much faster than what you would get using any RDBMS.

Additionally, you can speed up your query performance even more by adding an index on the phoneNumbers attribute.

 

findByCompanyAndAreaId

In the query above we are basically trying to find the root node (Building) giving a random child node (Area). Our data is structured as a tree because an Area could also have a list of other areas:

 

This type of querying is one of the most expensive and complex operations when you are working with a relational database, in most of the cases you either find the root node by hand or using some big fat query with UNIONs and CONNECTED BYs.

Here you can solve this problem by using a magical keyword called WITHIN.

 

Services

By default, you will inject and use your repositories in your Services like you normally would, but you can additionally access Couchbase’s specific repositories capabilities using the method getCouchbaseOperations()

 

Everything in Action

 

Using  services are to exactly what you would expect:

 

Check out the integration test class BuildingServiceIntegrationTest to see everything in action.

If you have any questions, tweet me at @deniswsrosa or ask a question on our forum

 

Posted by Denis Rosa, Developer Advocate, Couchbase

Denis Rosa is a Developer Advocate for Couchbase and lives in Munich - Germany. He has a solid experience as a software engineer and speaks fluently Java, Python, Scala and Javascript. Denis likes to write about search, Big Data, AI, Microservices and everything else that would help developers to make a beautiful, faster, stable and scalable app.

5 Comments

  1. This is a great blog! I am pretty new to Spring and Spring boot . And we are setting up CouchBase . I have the CouchBase running locally in my computer, However did not find any provision for giving password for the bucket. And hence when I am running my application its giving me InvalidPasswordException: Passwords for bucket do not match.

    Could you please let me know how do I resolve this

    1. Denis Rosa, Developer Advocate, CouchbaseNovember 30, 2018 at 2:16 am

      Hi Kn,

      The easiest way is to create a user with the same name as your bucket, and then you can follow this tutorial
      https://blog.couchbase.com/couchbase-spring-boot-spring-data/

      If you want to have multiple users accessing the same bucket, you will need to implement the AbstractCouchbaseConfiguration class

      https://stackoverflow.com/questions/53177777/couchbase-6-0-springboot-invalidpasswordexception

  2. I am pretty new to Couchbase and spring data ,Spring boot implementation. I am using document subdocument API to update,Insert and remove the subdocument without reading the whole document. I can code the path to subdocument and do mutation operations directly on the subdocument. One of the advantage with document subdocument API is I can act on subdocument without locking the whole document(In my use case).

    How can I achieve the same using spring-data and Spring boot . I see the major difference where spring-data works with N1QL query and POJO where as document subdocument API work with Json.

    Reference to document Subdocument API: https://docs.couchbase.com/java-sdk/2.7/subdocument-operations.html

  3. Denis Rosa, Developer Advocate, CouchbaseNovember 30, 2018 at 2:32 am

    Hi Muraic,

    Subdocuments are usually faster, as you are get the document by its key. However, Spring data is much more productive.

    You can use spring data and still be able to make subdocument operations, if you call yourRepository.getCouchbaseOperations().getCouchbaseBucket() you will get access to the Bucket object, which potentially is the one you are using right now.

Leave a reply