CRUD Java Application with Couchbase, Java EE and WildFly
Couchbase is an open-source, NoSQL, document database. It allows to access, index, and query JSON documents while taking advantage of integrated distributed caching for high performance data access.
Developers can write applications to Couchbase using different languages (Java, Go, .NET, Node, PHP, Python, C) multiple SDKs. This blog will show how you can easily create a CRUD application using Java SDK for Couchbase.
REST with Couchbase
The application will use curl
to issue REST commands to a JAX-RS endpoint deployed on WildFly. These commands will then perform CRUD operations on travel-sample
bucket in Couchbase. N1QL (SQL query language for JSON) will be used to communicate with Couchbase to retrieve results. Both the “builder pattern” and raw N1QL commands will be used.
TL;DR
Complete source code and instructions for the sample are available at github.com/arun-gupta/couchbase-javaee.
Lets get started!
Run Couchbase Server
Couchbase server can be easily downloaded from Couchbase Server Downloads page. In a containerized world, its a lot easier to fire up a Couchbase server using Docker.
If Docker is configured on your machine then the easiest way is to use Docker Compose for Couchbase:
mycouchbase: name: mycouchbase image: couchbase/server volumes: - ~/couchbase:/opt/couchbase/var ports: - 8091:8091 - 8092:8092 - 8093:8093 - 11210:11210
Starting up the application server shows:
> docker-compose up -d Creating couchbaseserver_mycouchbase_1
And then the logs can be seen as:
> docker-compose logs Attaching to couchbaseserver_mycouchbase_1 mycouchbase_1 | Starting Couchbase Server -- Web UI available at http://<ip>:8091
The database needs to be configured and is explained at Configure Couchbase Server. Make sure to install travel-sample
bucket.
Deploy the Java EE Application on WildFly
- Download WildFly 9.0.2 , unzip, and start WildFly application server as
./wildfly-9.0.0.Final/bin/standalone.sh
. - Git clone the repo:
git clone https://github.com/arun-gupta/couchbase-javaee.git
- Change directory
cd couchbase-javaee
- Deploy the application to WildFly:
mvn install -Pwildfly
.
The application uses Java SDK for Couchbase by importing the following Maven coordinates:
<dependency> <groupId>com.couchbase.client</groupId> <artifactId>java-client</artifactId> <version>2.2.1</version> </dependency>
Invoke the REST Endpoints Using cURL
GET Airline resources (limit to 10)
Lets query the database to list 10 Airline resources.
Request
~ > curl -v http://localhost:8080/couchbase-javaee/resources/airline * Hostname was NOT found in DNS cache * Trying ::1... * connect to ::1 port 8080 failed: Connection refused * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8080 (#0) > GET /couchbase-javaee/resources/airline HTTP/1.1 > User-Agent: curl/7.37.1 > Host: localhost:8080 > Accept: */* >
Response
< HTTP/1.1 200 OK < Connection: keep-alive < X-Powered-By: Undertow/1 * Server WildFly/9 is not blacklisted < Server: WildFly/9 < Content-Type: application/octet-stream < Content-Length: 1415 < Date: Wed, 18 Nov 2015 21:19:15 GMT < * Connection #0 to host localhost left intact [{"travel-sample":{"country":"France","iata":"SB","callsign":"AIRCALIN","name":"Air Caledonie International","icao":"ACI","id":139,"type":"airline"}}, {"travel-sample":{"country":"United States","iata":"WQ","callsign":null,"name":"PanAm World Airways","icao":"PQW","id":13633,"type":"airline"}}, {"travel-sample":{"country":"United Kingdom","iata":"BA","callsign":"SPEEDBIRD","name":"British Airways","icao":"BAW","id":1355,"type":"airline"}}, {"travel-sample":{"country":"United States","iata":"FL","callsign":"CITRUS","name":"AirTran Airways","icao":"TRS","id":1316,"type":"airline"}}, {"travel-sample":{"country":"United States","iata":"-+","callsign":null,"name":"U.S. Air","icao":"--+","id":13391,"type":"airline"}}, {"travel-sample":{"country":"United States","iata":"Q5","callsign":"MILE-AIR","name":"40-Mile Air","icao":"MLA","id":10,"type":"airline"}}, {"travel-sample":{"country":"France","iata":"AF","callsign":"AIRFRANS","name":"Air France","icao":"AFR","id":137,"type":"airline"}}, {"travel-sample":{"country":"United States","iata":"K5","callsign":"SASQUATCH","name":"SeaPort Airlines","icao":"SQH","id":10765,"type":"airline"}}, {"travel-sample":{"country":"France","iata":"A5","callsign":"AIRLINAIR","name":"Airlinair","icao":"RLA","id":1203,"type":"airline"}}, {"travel-sample":{"country":"United Kingdom","iata":"5W","callsign":"FLYSTAR","name":"Astraeus","icao":"AEU","id":112,"type":"airline"}}]
The N1QL query for this is written as:
N1qlQuery query = N1qlQuery .simple(select("*") .from(i(database.getBucket().name())) .limit(10));
And can also be alternatively written as:
SELECT * FROM `travel-sample` LIMIT 10
You may optionally update the code to include ORDER BY clause as shown in N1QL Tutorial.
GET one Airline resource
Use id
attribute to query a single Airline resource
Request
~ > curl -v http://localhost:8080/couchbase-javaee/resources/airline/139 * Hostname was NOT found in DNS cache * Trying ::1... * connect to ::1 port 8080 failed: Connection refused * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8080 (#0) > GET /couchbase-javaee/resources/airline/139 HTTP/1.1 > User-Agent: curl/7.37.1 > Host: localhost:8080 > Accept: */* >
Response
< HTTP/1.1 200 OK < Connection: keep-alive < X-Powered-By: Undertow/1 * Server WildFly/9 is not blacklisted < Server: WildFly/9 < Content-Type: application/octet-stream < Content-Length: 148 < Date: Wed, 18 Nov 2015 21:23:34 GMT < * Connection #0 to host localhost left intact {"travel-sample":{"country":"France","iata":"SB","callsign":"AIRCALIN","name":"Air Caledonie International","icao":"ACI","id":139,"type":"airline"}}
POST a new Airline resource
Learn how to run N1QL queries from the CLI using CBQ tool and verify the existing sample data:
bin > ./cbq -engine=http://192.168.99.100:8093 Couchbase query shell connected to http://192.168.99.100:8093/ . Type Ctrl-D to exit. cbq> select * from `travel-sample` where name="Airlinair" limit 10; { "requestID": "ce2de67b-2c05-47df-afbe-343cb7409d2b", "signature": { "*": "*" }, "results": [ { "travel-sample": { "callsign": "AIRLINAIR", "country": "France", "iata": "A5", "icao": "RLA", "id": 1203, "name": "Airlinair", "type": "airline" } } ], "status": "success", "metrics": { "elapsedTime": "3.418285894s", "executionTime": "3.418232688s", "resultCount": 1, "resultSize": 294 } }
This query retrieve documents where airline’s name is Airlinair
. The count is shown in metrics.resultCount
.
Create a new document using POST.
Request
~ > curl -v -H "Content-Type: application/json" -X POST -d '{"country":"France","iata":"A5","callsign":"AIRLINAIR","name":"Airlinair","icao":"RLA","type":"airline"}' http://localhost:8080/couchbase-javaee/resources/airline * Hostname was NOT found in DNS cache * Trying ::1... * connect to ::1 port 8080 failed: Connection refused * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8080 (#0) > POST /couchbase-javaee/resources/airline HTTP/1.1 > User-Agent: curl/7.37.1 > Host: localhost:8080 > Accept: */* > Content-Type: application/json > Content-Length: 104 >
Response
* upload completely sent off: 104 out of 104 bytes < HTTP/1.1 200 OK < Connection: keep-alive < X-Powered-By: Undertow/1 * Server WildFly/9 is not blacklisted < Server: WildFly/9 < Content-Type: application/octet-stream < Content-Length: 117 < Date: Wed, 18 Nov 2015 21:42:51 GMT < * Connection #0 to host localhost left intact {"country":"France","iata":"A5","callsign":"AIRLINAIR","name":"Airlinair","icao":"RLA","id":"19810","type":"airline"}
Query again using CBQ and now the results are shown as:
cbq> select * from `travel-sample` where name="Airlinair" limit 10; { "requestID": "5e79031a-f7ee-4cc9-8c87-4e3b7484f09f", "signature": { "*": "*" }, "results": [ { "travel-sample": { "callsign": "AIRLINAIR", "country": "France", "iata": "A5", "icao": "RLA", "id": 1203, "name": "Airlinair", "type": "airline" } }, { "travel-sample": { "callsign": "AIRLINAIR", "country": "France", "iata": "A5", "icao": "RLA", "id": "19810", "name": "Airlinair", "type": "airline" } } ], "status": "success", "metrics": { "elapsedTime": "3.342391947s", "executionTime": "3.342343455s", "resultCount": 2, "resultSize": 591 } }
Note that two JSON documents are returned instead of one as before the POST command was issued.
PUT an existing Airline resource
Update an existing resource using HTTP POST.
The data model for travel-sample
bucket requires to include “id” attribute in the payload and in the URI as well.
Request
~ > curl -v -H "Content-Type: application/json" -X PUT -d '{"country":"France","iata":"A5","callsign":"AIRLINAIR","name":"Airlin Air","icao":"RLA","type":"airline","id": "19810"}' http://localhost:8080/couchbase-javaee/resources/airline/19810 * Hostname was NOT found in DNS cache * Trying ::1... * connect to ::1 port 8080 failed: Connection refused * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8080 (#0) > PUT /couchbase-javaee/resources/airline/19810 HTTP/1.1 > User-Agent: curl/7.37.1 > Host: localhost:8080 > Accept: */* > Content-Type: application/json > Content-Length: 118 > * upload completely sent off: 118 out of 118 bytes
Name of the airline is updated from “Airlinair” to “Airlin Air”, all other attributes stay the same.
Response
< HTTP/1.1 200 OK < Connection: keep-alive < X-Powered-By: Undertow/1 * Server WildFly/9 is not blacklisted < Server: WildFly/9 < Content-Type: application/octet-stream < Content-Length: 117 < Date: Wed, 18 Nov 2015 21:46:16 GMT < * Connection #0 to host localhost left intact {"country":"France","iata":"A5","callsign":"AIRLINAIR","name":"Airlin Air","icao":"RLA","id":"19810","type":"airline"}
The updated record is shown in the response.
Querying for Airlinair
gives:
cbq> select * from `travel-sample` where name="Airlinair" limit 10; { "requestID": "a8d72427-9f4b-49ab-a77a-17cd99cdce5f", "signature": { "*": "*" }, "results": [ { "travel-sample": { "callsign": "AIRLINAIR", "country": "France", "iata": "A5", "icao": "RLA", "id": 1203, "name": "Airlinair", "type": "airline" } } ], "status": "success", "metrics": { "elapsedTime": "3.372603693s", "executionTime": "3.37256091s", "resultCount": 1, "resultSize": 294 } }
So the previously added record is now updated and thus does not appear in query results. Querying for Airlin Air
gives:
cbq> select * from `travel-sample` where name="Airlin Air" limit 10; { "requestID": "a3797a73-d879-4ca1-be90-e07179aae118", "signature": { "*": "*" }, "results": [ { "travel-sample": { "callsign": "AIRLINAIR", "country": "France", "iata": "A5", "icao": "RLA", "id": "19810", "name": "Airlin Air", "type": "airline" } } ], "status": "success", "metrics": { "elapsedTime": "3.393649025s", "executionTime": "3.393530368s", "resultCount": 1, "resultSize": 298 } }
This shows the newly updated document.
DELETE an existing Airline resource
Query for a unique id:
cbq> select * from `travel-sample` where id="19810" limit 10; { "requestID": "47a315cd-afe4-45a8-8814-5ab3034e0d0f", "signature": { "*": "*" }, "results": [ { "travel-sample": { "callsign": "AIRLINAIR", "country": "France", "iata": "A5", "icao": "RLA", "id": "19810", "name": "Airlin Air", "type": "airline" } } ], "status": "success", "metrics": { "elapsedTime": "3.006863656s", "executionTime": "3.006821997s", "resultCount": 1, "resultSize": 298 } }
Notice that one document is returned.
Lets delete this document.
Request
~ > curl -v -X DELETE http://localhost:8080/couchbase-javaee/resources/airline/19810 * Hostname was NOT found in DNS cache * Trying ::1... * connect to ::1 port 8080 failed: Connection refused * Trying 127.0.0.1... * Connected to localhost (127.0.0.1) port 8080 (#0) > DELETE /couchbase-javaee/resources/airline/19810 HTTP/1.1 > User-Agent: curl/7.37.1 > Host: localhost:8080 > Accept: */* >
Response
> HTTP/1.1 200 OK > Connection: keep-alive > X-Powered-By: Undertow/1 * Server WildFly/9 is not blacklisted > Server: WildFly/9 > Content-Type: application/octet-stream > Content-Length: 136 > Date: Wed, 18 Nov 2015 21:52:47 GMT > * Connection #0 to host localhost left intact {"travel-sample":{"country":"France","iata":"A5","callsign":"AIRLINAIR","name":"Airlin Air","icao":"RLA","id":"19810","type":"airline"}}
The deleted document is shown in the response.
Query again for the deleted id:
cbq> select * from `travel-sample` where id="19810" limit 10; { "requestID": "972b0bbd-ba25-4f6c-a30e-ed188bf43588", "signature": { "*": "*" }, "results": [ ], "status": "success", "metrics": { "elapsedTime": "3.261481199s", "executionTime": "3.261431917s", "resultCount": 0, "resultSize": 0 } }
And no results are returned!
- As mentioned earlier, the complete code base is at github.com/arun-gupta/couchbase-javaee.
Enjoy!
Reference: | CRUD Java Application with Couchbase, Java EE and WildFly from our JCG partner Arun Gupta at the Miles to go 2.0 … blog. |