Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to Use Couchbase With Java

DZone's Guide to

How to Use Couchbase With Java

Learn how to use Couchbase with Java by creating a heroes service that will come up with a superhero's name, their powers, and more.

· Database Zone ·
Free Resource

RavenDB vs MongoDB: Which is Better? This White Paper compares the two leading NoSQL Document Databases on 9 features to find out which is the best solution for your next project.  

Couchbase is an open-source NoSQL database with support for key-value and document types — so, it's a multimodel NoSQL database. This database also has nice features such as N1QL, which is a kind of SQL query for JSON values. Couchbase even has a vibrant and bright UI for database management.

Nevertheless, Couchbase is still unknown to many Java developers. This post will give a brief review of this database and how to integrate it with Java and Java EE. We will cover just one implementation of Couchbase: the document type. To make this example more didactic, we will create a sample code. This sample code will provide a heroes service, which will provide the hero's name, their powers, and more. This heroes service exposes a CRUD operation plus simple queries through resources with JAX-RS.

Minimum Requirements

Installing Couchbase

Once Docker is configured, just run the docker command:

docker run -d --name couchbase-instance -p 8091-8094:8091-8094 -p 11210:11210 couchbase

Next, visit http://localhost:8091 on your host machine to see the web console and start Couchbase Server setup.

Image title

To make configuration easier, use user: root and password: 123456.

Image title

Image title

To get more information, check out this Couchbase documentation from Docker.

Create a Heroes Bucket in Couchbase

With all the configurations set, the next step is to create a heroes bucket. Go to the Add Data Bucket section and select Add Bucket.

Image title

Then, go to Query Editor section and hit Execute to create an index for this bucket: 

CREATE PRIMARY INDEX index_heroes on heroes;

Image title

Set the Dependencies for the Project

This project will a simple Maven project with the Java EE 7 API set as provided to connect to the Couchbase database. JNoSQL will be configured. Likewise, the project has two new dependencies:

<dependency>
    <groupId>org.jnosql.artemis</groupId>
    <artifactId>artemis-document</artifactId>
    <version>${jnosql.version}</version>
</dependency>

<dependency>
    <groupId>org.jnosql.diana</groupId>
    <artifactId>couchbase-driver</artifactId>
    <version>${jnosql.version}</version>
</dependency>
  1. The first is the Mapping API (think JPA) to document database types. This Mapping API will do conversions of an entity object to the Communication API.

  2. The second is the Communication API for Couchbase (think JDBC). This project is just an adapter to the JNoSQL Communication API, so it uses the official Couchbase driver.

Modeling

Our heroes will have a name, real name, age, and list of powers. A critical behavior from the NoSQL type, mainly the document one, is that we can have a field that can be a list set. Furthermore, we can arrange an attribute as a set of strings.

@Entity
public class Hero implements Serializable {

    @Id
    private String id;

    @Column
    private String name;

    @Column
    private String realName;

    @Column
    private Integer age;

    @Column
    private Set<String> powers;


}

Make the Document Connection Available to JNoSQL

The point here is to make a DocumentCollectionManager instance available to the CDI so that the Mapping API can use it.

@ApplicationScoped
public class DocumentCollectionManagerProducer {


    private static final String DOCUMENT_COLLECTION = "heroes";

    private DocumentCollectionManager entityManager;

    @PostConstruct
    public void setUp() {
        DocumentConfiguration<?> configuration = new CouchbaseDocumentConfiguration();
        Settings settings = Settings.builder()
                .put("couchbase-host-1", "localhost")
                .put("couchbase-user", "root")
                .put("couchbase-password", "123456").build();
        DocumentCollectionManagerFactory<?> managerFactory = configuration.get(settings);
        entityManager = managerFactory.get(DOCUMENT_COLLECTION);
    }

    @Produces
    public DocumentCollectionManager getEntityManager() {
        return entityManager;
    }
}

The CRUD Operations

Beyond the CRUD operations, this resource is going to offer queries to both senior and young heroes. This sample will use a Repository interface from JNoSQL (meaning an interface that extends the Repository).

Once the HeroRepository extends this Repository, it will cover the CRUD operations. But that still leaves the younger and senior heroes operations. Luckily, there is method query resource, which will do a native query on the method name just by using the findBy suffix.

public interface HeroRepository extends Repository<Hero, String> {

    List<Hero> findAll();

    List<Hero> findByAgeGreaterThan(Integer age);

    List<Hero> findByAgeLessThan(Integer age);
}

The Resource

Finally, here's how to expose the Repository with JAX-RS:

@ApplicationScoped
@Path("heroes")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class HeroResource {

    private static final Supplier<WebApplicationException> NOT_FOUND =
            () -> new WebApplicationException(Response.Status.NOT_FOUND);


    @Inject
    @Database(DOCUMENT)
    private HeroRepository repository;

    @GET
    public List<Hero> findAll() {
        return repository.findAll();
    }

    @GET
    @Path("/{id}")
    public Hero findById(@PathParam("id") String id) {
        return repository.findById(id).orElseThrow(NOT_FOUND);
    }

    @GET
    @Path("seniors/{age}")
    public List<Hero> findByOlder(@PathParam("age") Integer age) {
        return repository.findByAgeGreaterThan(age);
    }

    @GET
    @Path("youngs/{age}")
    public List<Hero> findByYounger(@PathParam("age") Integer age) {
        return repository.findByAgeLessThan(age);
    }


    @POST
    public void save(Hero hero) {
        repository.save(hero);
    }

    @PUT
    @Path("/{id}")
    public void update(@PathParam("id") String id, Hero hero) {
        repository.save(hero);
    }

    @Path("/{id}")
    @DELETE
    public void delete(@PathParam("id") String name) {
        repository.deleteById(name);
    }
}

Let's Test It

Now, it's time to have fun with this resource. You can either make an executable JAR or a WAR to put on the server. This post will use an executable JAR with the TomEE plugin:

mvn clean package tomee:exec -DskipTests
java -jar target/heroes-1.0-SNAPSHOT-exec.jar

Return all heroes with the curl command:

curl -H "Content-Type: application/json" -X POST -d '{"realName": "Tony Stark", "name": "iron_man","powers": ["rich"],"age": 34}' http://localhost:8080/heroes/resource/heroes/
curl -H "Content-Type: application/json" -X POST -d '{"realName": "Steve Rogers", "name": "captain_america","powers": ["strong", "shield"],"age": 80}' http://localhost:8080/heroes/resource/heroes/
curl -H "Content-Type: application/json" -X POST -d '{"realName": "Bruce Banner", "name": "hulk","powers": ["strong", "smash"],"age": 30}' http://localhost:8080/heroes/resource/heroes/
curl -H "Content-Type: application/json" -X POST -d '{"realName": "Thor", "name": "thor","powers": ["strong", "Hammer"],"age": 2000}' http://localhost:8080/heroes/resource/heroes/
curl -H "Content-Type: application/json" -X POST -d '{"realName": "Natasha Romanov", "name": "black_widow","powers": ["spy", "Acrobat"],"age": 37}' http://localhost:8080/heroes/resource/heroes/
curl -H "Content-Type: application/json" -X POST -d '{"realName": "T Challa", "name": "black_panter","powers": ["spy", "Acrobat", "Strong"],"age": 42}' http://localhost:8080/heroes/resource/heroes/
curl -H "Content-Type: application/json" -X POST -d '{"realName": "Clint Barton", "name": "hawkeye","powers": ["none"],"age": 42}' http://localhost:8080/heroes/resource/heroes/
curl -H "Content-Type: application/json" -X POST -d '{"realName": "Peter Parker", "name": "spider-man","powers": ["climp", "Strong", "spider"],"age": 18}' http://localhost:8080/heroes/resource/heroes/

Now, have fun using either any HTTP client or just a browser with these GET resources:

  • http://localhost:8080/heroes/resource/heroes/

  • http://localhost:8080/heroes/resource/heroes/youngs/20

  • http://localhost:8080/heroes/resource/heroes/seniors/30

Going Beyond the Standard JNoSQL API With Extensions

Couchbase has nice features such as N1QL, which is a relational, SQL-based JSON query, as well as full-text search, which makes text search faster and more efficient than the wildcard query. In the NoSQL world, any particular provider behavior matters. That's why there are extensions: to act as a bridge between these specific features and JNoSQL.

    interface HeroRepository extends CouchbaseRepository<Hero, String> {

        @N1QL("select * from Hero")
        List<Hero> findAll();

        @N1QL("select * from Hero where realName = $realName")
        List<Hero> findByName(@Param("realName") String realName);
    }
CouchbaseTemplate template = ...;
List<Hero> heroes = template.n1qlQuery("select * from Hero where realName = $realName", params);

MatchQuery match = SearchQuery.match("rich");
SearchQuery query = new SearchQuery("index-heroes", match);
List<Hero> heroes = template.search(query);

Get more information about the Couchbase extension here.




The whole code sample

Get comfortable using NoSQL in a free, self-directed learning course provided by RavenDB. Learn to create fully-functional real-world programs on NoSQL Databases. Register today.

Topics:
couchbase ,tutorial ,java development ,jnosql ,dependencies ,buckets

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}