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.
Join the DZone community and get the full member experience.
Join For FreeCouchbase 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
Java 8+
Maven
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.
To make configuration easier, use user: root and password: 123456.
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.
Then, go to Query Editor section and hit Execute to create an index for this bucket:
CREATE PRIMARY INDEX index_heroes on heroes;
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>
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.
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.
Opinions expressed by DZone contributors are their own.
Comments