[![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.jooby/jooby-couchbase/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.jooby/jooby-couchbase) [![javadoc](https://javadoc.io/badge/org.jooby/jooby-couchbase.svg)](https://javadoc.io/doc/org.jooby/jooby-couchbase/1.4.1) [![jooby-couchbase website](https://img.shields.io/badge/jooby-couchbase-brightgreen.svg)](http://jooby.org/doc/couchbase) # couchbase Couchbase is a NoSQL document database with a distributed architecture for performance, scalability, and availability. It enables developers to build applications easier and faster by leveraging the power of SQL with the flexibility of JSON. This module provides couchbase access via Java SDK ## dependency ```xml org.jooby jooby-couchbase 1.4.1 ``` ## exports * `CouchbaseEnvironment` * `CouchbaseCluster` * `AsyncBucket` * `Bucket` * `AsyncDatastore` * `Datastore` * `AsyncRepository` * `Repository` * Optionally a couchbase `Session.Store` ## usage Via couchbase connection string: ```java { use(new Couchbase("couchbase://locahost/beers")); get("/", req -> { Bucket beers = require(Bucket.class); // do with beer bucket }); } ``` Via property: ```java { use(new Couchbase("db")); get("/", req -> { Bucket beers = require(Bucket.class); // do with beer bucket }); } ``` The ```db``` property is defined in the ```application.conf``` file as a couchbase connection string. ## create, read, update and delete Jooby provides a more flexible, easy to use and powerful CRUD operations via [AsyncDatastore]({{defdocs/couchbase/AsyncDatastore.html)/[AsyncDatastore]({{defdocs/couchbase/Datastore.html) objects. ```java import org.jooby.couchbase.Couchbase; import org.jooby.couchbase.N1Q; ... { use(new Couchbase("couchbase://localhost/beers")); use("/api/beers") /** List beers */ .get(req -> { Datastore ds = require(Datastore.class); return ds.query(N1Q.from(Beer.class)); }) /** Create a new beer */ .post(req -> { Datastore ds = require(Datastore.class); Beer beer = req.body().to(Beer.class); return ds.upsert(beer); }) /** Get a beer by ID */ .get(":id", req -> { Datastore ds = require(Datastore.class); return ds.get(Beer.class, req.param("id").value()); }) /** Delete a beer by ID */ .delete(":id", req -> { Datastore ds = require(Datastore.class); return ds.delete(Beer.class, req.param("id").value()); }); } ``` As you can see benefits over `AsyncRepository`/`Repository` are clear: you don't have to deal with `EntityDocument` just send or retrieve POJOs. Another good reason is that [AsyncDatastore]({{defdocs/couchbase/AsyncDatastore.html)/[AsyncDatastore]({{defdocs/couchbase/Datastore.html) supports query operations with POJOs too. ### design and implementation choices The [AsyncDatastore]({{defdocs/couchbase/AsyncDatastore.html)/[AsyncDatastore]({{defdocs/couchbase/Datastore.html) simplifies a lot the integration between Couchbase and POJOs. This section describes how IDs are persisted and how mapping works. A document persisted by an [AsyncDatastore]({{defdocs/couchbase/AsyncDatastore.html)/[AsyncDatastore]({{defdocs/couchbase/Datastore.html) looks like: ```java { "model.Beer::1": { "name": "IPA", ... "id": 1, "_class": "model.Beer" } } ``` The couchbase document ID contains the fully qualified name of the class, plus ```::``` plus the entity/business ID: ```mode.Beer::1```. The business ID is part of the document, here the business ID is: ```id:1```. The business ID is required while creating POJO from couchbase queries. Finally, a ```_class``` attribute is also part of the document. It contains the fully qualified name of the class and its required while creating POJO from couchbase queries. ### mapping pojos Mapping between document/POJOs is done internally with a custom `EntityConverter`. The `EntityConverter` uses an internal copy of ```ObjectMapper``` object from [Jackson](https://github.com/FasterXML/jackson). So in **theory** anything that can be handle by [Jackson](https://github.com/FasterXML/jackson) will work. In order to work with a POJO, you must defined an ID. There are two options: * Add an ```id``` field to your POJO: ```java public class Beer { private String id; } ``` * Use a business name (not necessarily id) and add ```Id``` annotation: ```java import import com.couchbase.client.java.repository.annotation.Id; ... public class Beer { @Id private String beerId; } ``` Auto-increment IDs are supported via [GeneratedValue](/apidocs/org/jooby/couchbase/GeneratedValue.html): ```java public class Beer { private Long id; } ``` Auto-increment IDs are generated using {@link Bucket#counter(String, long, long)} function and they must be ```Long```. We use the POJO fully qualified name as counter ID. Any other field will be mapped too, you don't need to annotate an attribute with {@link Field}. If you don't want to persist an attribute, just ad the ```transient``` Java modifier: ```java public class Beer { private String id; private transient ignored; } ``` Keep in mind that if you annotated your POJO with [Jackson](https://github.com/FasterXML/jackson) annotations they will be ignored... because we use an internal copy of [Jackson](https://github.com/FasterXML/jackson) that comes with ```Java Couchbase SDK``` ## reactive usage Couchbase SDK allows two programming model: ```blocking``` and ```reactive```. We already see how to use the blocking API, now is time to see how to use the ```reactive``` API: ```java { use(new Couchbase("couchbase://localhost/beers")); get("/", req -> { AsyncBucket bucket = require(AsyncBucket.class); // do with async bucket ;) }); } ``` Now, what to do with Observables? Do we have to block? Not necessarily if we use the [Rx](https://github.com/jooby-project/jooby/tree/master/jooby-rxjava) module: ```java ... import org.jooby.rx.Rx; ... { // handle observable route responses use(new Rx()); use(new Couchbase("couchbase://localhost/beers")); get("/api/beer/:id", req -> { AsyncDatastore ds = require(AsyncDatastore.class); String id = req.param("id").value(); Observable beer = ds.get(Beer.class, id); return beer; }); } ``` The [Rx](https://github.com/jooby-project/jooby/tree/master/jooby-rxjava) module deal with observables so you can safely return `Observable` from routes (Jooby rocks!). ## multiple buckets If for any reason your application requires more than 1 bucket... then: ```java { use(new Couchbase("couchbase://localhost/beers") .buckets("extra-bucket")); get("/", req -> { Bucket bucket = require("beers", Bucket.class); Bucket extra = require("extra-bucket", Bucket.class); }); } ``` Easy, right? Same principle apply for Async* objects ## multiple clusters Again, if for any reason your application requires multiple clusters... then: ```java { CouchbaseEnvironment env = ...; use(new Couchbase("couchbase://192.168.56.1") .environment(env)); use(new Couchbase("couchbase://192.168.57.10") .environment(env)); } ``` You must shared the `CouchbaseEnvironment` as documented here. ## options ### bucket password You can set a global bucket password via: ```couchbase.bucket.password``` property, or local bucket password (per bucket) via ```couchbase.bucket.[name].password``` property. ### environment configuration Environment configuration is available via: ```couchbase.env``` namespace, here is an example on how to setup ```kvEndpoints```: ``` couchbase.env.kvEndpoints = 3 ``` ### cluster manager A `ClusterManager` service is available is you set an cluster username and password: ``` couchbase.cluster.username = foo couchbase.cluster.password = bar ``` ## couchbase.conf These are the default properties for couchbase: ```properties # configure the environment by adding properties to the `couchbase.env` namespace, like: # couchbase.env.kvEndpoints = 2 # Some custom error codes err { com.couchbase.client.java.error.BucketAlreadyExistsException = 400 com.couchbase.client.java.error.BucketDoesNotExistException = 404 com.couchbase.client.java.error.DocumentAlreadyExistsException = 400 com.couchbase.client.java.error.DocumentDoesNotExistException = 404 com.couchbase.client.java.error.QueryExecutionException = 400 } ``` # couchbase session store A [Session.Store](/apidocs/org/jooby/couchbase/CouchbaseSessionStore) powered by Couchbase. ## dependency ```xml org.jooby jooby-couchbase 1.4.1 ``` ## usage ```java { use(new Couchbase("couchbase://localhost/bucket")); session(CouchbaseSessionStore.class); get("/", req -> { Session session = req.session(); session.put("foo", "bar"); .. }); } ``` Session data is persisted in Couchbase and document looks like: ``` { "session::{SESSION_ID}": { "foo": "bar" } } ``` ## options ### timeout By default, a session will expire after ```30 minutes```. Changing the default timeout is as simple as: ```properties # 8 hours session.timeout = 8h # 15 seconds session.timeout = 15 # 120 minutes session.timeout = 120m ``` Expiration is done via Couchbase expiry/ttl option. If no timeout is required, use ```-1```. ### custom bucket The session document are persisted in the application/default bucket, if you need/want a different bucket then use {@link Couchbase#sessionBucket(String)}, like: ```java { use( new Couchbase("couchbase://localhost/myapp") .sessionBucket("session") ); } ```