Skip to content

Commit 67fdab6

Browse files
author
chedim
committed
openapi
1 parent 0c9215f commit 67fdab6

File tree

2 files changed

+253
-3
lines changed

2 files changed

+253
-3
lines changed

README.md

Lines changed: 252 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,252 @@
1-
# java-springdata-quickstart
2-
Example Spring Data Couchbase application
1+
[![Try it now!](https://da-demo-images.s3.amazonaws.com/runItNow_outline.png?couchbase-example=java-springdata-quickstart-repo&source=github)](https://gitpod.io/#https://github.com/couchbase-examples/java-springdata-quickstart)
2+
3+
## Overview
4+
This quickstart tutorial will review the basics of using Couchbase by building a simple Spring Data REST API that stores user profiles is used as an example.
5+
6+
## What We'll Cover
7+
- [Cluster Connection Configuration](#cluster-connection-configuration) – Configuring Spring Data to connect to a Couchbase cluster.
8+
- [Database Initialization](#database-initialization) – Creating required database structures upon application startup
9+
- [CRUD operations](#create-or-update-a-profile) – Standard create, update and delete operations.
10+
- [Custom SQL++ queries](#search-profiles-by-text) – Using [SQl++](https://www.couchbase.com/sqlplusplus) with Spring Data.
11+
12+
## Useful Links
13+
- [Spring Data Couchbase - Reference Documentation](https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/)
14+
- [Spring Data Couchbase - JavaDoc](https://docs.spring.io/spring-data/couchbase/docs/current/api/)
15+
16+
## Prerequisites
17+
To run this prebuild project, you will need:
18+
- [Couchbase Capella](https://docs.couchbase.com/cloud/get-started/create-account.html) account or locally installed [Couchbase Server](/tutorial-couchbase-installation-options)
19+
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
20+
- Code Editor or an Integrated Development Environment (e.g., [Eclipse](https://www.eclipse.org/ide/))
21+
- [Java SDK v1.8 or higher installed](https://www.oracle.com/java/technologies/ee8-install-guide.html)
22+
- [Gradle Build Tool](https://gradle.org/install/)
23+
24+
## Source Code
25+
The sample source code used in this tutorial is [published on GitHub](https://github.com/couchbase-examples/java-springboot-quickstart).
26+
To obtain it, clone the git repository with your IDE or execute the following command:
27+
```shell
28+
git clone https://github.com/couchbase-examples/java-springdata-quickstart
29+
```
30+
## Dependencies
31+
Gradle dependencies:
32+
```groovy
33+
implementation 'org.springframework.boot:spring-boot-starter-web'
34+
// spring data couchbase connector
35+
implementation 'org.springframework.boot:spring-boot-starter-data-couchbase'
36+
// swagger ui
37+
implementation 'org.springdoc:springdoc-openapi-ui:1.6.6'
38+
```
39+
40+
Maven dependencies:
41+
```xml
42+
<dependency>
43+
<groupId>org.springframework.boot</groupId>
44+
<artifactId>spring-boot-starter-data-couchbase</artifactId>
45+
</dependency>
46+
<dependency>
47+
<groupId>org.springframework.boot</groupId>
48+
<artifactId>spring-boot-starter-web</artifactId>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.springdoc</groupId>
52+
<artifactId>springdoc-openapi-ui</artifactId>
53+
<version>1.6.6</version>
54+
</dependency>
55+
```
56+
57+
## Cluster Connection Configuration
58+
Spring Data couchbase connector can be configured by providing a `@Configuration` [bean](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-definition) that extends [`AbstractCouchbaseConfiguration`](https://docs.spring.io/spring-data/couchbase/docs/current/api/org/springframework/data/couchbase/config/AbstractCouchbaseConfiguration.html).
59+
The sample application provides a configuration bean that uses default couchbase login and password:
60+
```java
61+
@Configuration
62+
public class CouchbaseConfiguration extends AbstractCouchbaseConfiguration {
63+
64+
@Override
65+
public String getConnectionString() {
66+
// capella
67+
// return "couchbases://cb.jnym5s9gv4ealbe.cloud.couchbase.com";
68+
69+
// localhost
70+
return "127.0.0.1"
71+
}
72+
73+
@Override
74+
public String getUserName() {
75+
return "Administrator";
76+
}
77+
78+
@Override
79+
public String getPassword() {
80+
return "password";
81+
}
82+
83+
@Override
84+
public String getBucketName() {
85+
return "springdata_quickstart";
86+
}
87+
88+
...
89+
```
90+
> *from config/CouchbaseConfiguration.java*
91+
92+
This default configuration assumes that you have a locally running Couchbae server and uses standard administrative login and password for demonstration purpose.
93+
Applications deployed to production or staging environments should use less privileged credentials created using [Role-Based Access Control](https://docs.couchbase.com/go-sdk/current/concept-docs/rbac.html).
94+
Please refer to [Managing Connections using the Java SDK with Couchbase Server](https://docs.couchbase.com/java-sdk/current/howtos/managing-connections.html) for more information on Capella and local cluster connections.
95+
96+
# Running the Application
97+
98+
To install dependencies and run the application on Linux, Unix or OS X, execute `./gradlew bootRun` (`./gradew.bat bootRun` on Windows).
99+
100+
Once the site is up and running, you can launch your browser and go to the [Swagger Start Page](http://localhost:8080/swagger-ui/) to test the APIs.
101+
102+
103+
## Document Structure
104+
We will be setting up a REST API to manage demo user profiles and store them as documents on a Couchbase Cluster. Every document needs a unique identifier with which it can be addressed in the API. We will use auto-generated UUIDs for this purpose and store in profile documents the first and the last name of the user, their age, and address:
105+
106+
```json
107+
{
108+
"id": "b181551f-071a-4539-96a5-8a3fe8717faf",
109+
"firstName": "John",
110+
"lastName": "Doe",
111+
"age": "35",
112+
"address": "123 Main St"
113+
}
114+
```
115+
116+
## Let's Review the Code
117+
118+
### Profile Model
119+
To work with submitted profiles, we first need to model their structure in a Java class, which would define the set of profile fields and their types.
120+
In our sample application, this is done in [`model/Profile.java`](https://github.com/couchbase-examples/java-springdata-quickstart/blob/main/src/main/java/trycb/model/Profile.java) class:
121+
122+
```java
123+
@Scope("_default")
124+
@Collection("profile")
125+
public class Profile {
126+
@id
127+
@GeneratedValue
128+
private UUID id;
129+
private String firstName, lastName;
130+
private byte age;
131+
private String address;
132+
133+
// ...
134+
}
135+
```
136+
> from `model/Profile.java`
137+
138+
The whole model is annotated with `@Scope` and `@Collection` annotations, which configure Spring Data to store model instances into `profile` collection in the default scope.
139+
140+
It is also worth noting the use of `@Id` and `@GeneratedValue` annotations on `Profile::id` field.
141+
142+
In couchbase, data is stored as JSON documents; each document has a unique identifier that can be used to address that document.
143+
Every profile instance in our example corresponds to a single document and this annotation is used here to link the document's id to a java field.
144+
Additionally, the `@GeneratedValue` annotation on the field instructs Spring Data to generate a random UUID if we try to store a profile without one, which will come in handy later.
145+
146+
You can find more information on key generation in the [Connector Documentation](https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/#couchbase.autokeygeneration).
147+
148+
Couchbase Spring Data connector will automatically serialize model instances into JSON when storing them on the cluster.
149+
150+
### Database initialization
151+
Automated database initialization and migration is a common solution that simplifies database management operations.
152+
To keep it simple, our demo uses [DbSetupRunner](https://github.com/couchbase-examples/java-springdata-quickstart/blob/main/src/main/java/trycb/config/DbSetupRunner.java) component class that implements `CommandLineRunner` interface and is invoked every time the application starts.
153+
The runner tries to create required structure every startup but ignores any errors if such structure already exists.
154+
For example, this code creates a primary index for configured bucket:
155+
```java
156+
try {
157+
// We must create primary index on our bucket in order to query it
158+
cluster.queryIndexes().createPrimaryIndex(config.getBucketName());
159+
LOGGER.info("Created primary index {}", config.getBucketName());
160+
} catch (IndexExistsException iee) {
161+
LOGGER.info("Primary index {} already exists", config.getBucketName());
162+
}
163+
```
164+
> From `config/DbSetupRunner.java`
165+
166+
Primary indexes in Couchbase contain all document keys and are used to fetch documents by their unique identifiers.
167+
Secondary indexes can be used to efficiently query documents by their properties.
168+
For example, `DbSetupRunner` creates additional indexes on the collection that allow querying profiles by first or last names or addresses:
169+
```java
170+
try {
171+
final String query = "CREATE INDEX secondary_profile_index ON " + config.getBucketName() + "._default." + CouchbaseConfiguration.PROFILE_COLLECTION + "(firstName, lastName, address)";
172+
cluster.query(query);
173+
} catch (IndexExistsException e) {
174+
LOGGER.info("Secondary index exists on collection {}", CouchbaseConfiguration.PROFILE_COLLECTION);
175+
}
176+
```
177+
> From `config/DbSetupRunner.java`
178+
179+
More information on working with Couchbase indexes can be found [in our documentation](https://docs.couchbase.com/server/current/learn/services-and-indexes/indexes/global-secondary-indexes.html).
180+
181+
### Create or update a Profile
182+
For CRUD operations, we will extend [`PagingAndSortingRepository`](https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/repository/PagingAndSortingRepository.html) provided by the framework:
183+
184+
```java
185+
@Repository
186+
public interface ProfileRepository extends PagingAndSortingRepository<Profile, UUID> {
187+
@Query("#{#n1ql.selectEntity} WHERE firstName LIKE '%' || $1 || '%' OR lastName LIKE '%' || $1 || '%' OR address LIKE '%' || $1 || '%' OFFSET $2 * $3 LIMIT $3")
188+
List<Profile> findByText(String query, int pageNum, int pageSize);
189+
190+
Page<Profile> findByAge(byte age, Pageable pageable);
191+
}
192+
```
193+
> From `repository/ProfileRepository.java`
194+
195+
Open the [`ProfileController`](https://github.com/couchbase-examples/java-springdata-quickstart/blob/main/src/main/java/trycb/controller/ProfileController.java) class located in `controller` package and navigate to the `saveProfile` method.
196+
This method accepts `Profile` objects deserialized by Spring Web from the body of an HTTP request.
197+
198+
```java
199+
@PostMapping("/profile")
200+
public ResponseEntity<Profile> saveProfile(@RequestBody Profile profile) {
201+
// the same endpoint can be used to create and save the object
202+
profile = profileRepository.save(profile);
203+
return ResponseEntity.status(HttpStatus.CREATED).body(profile);
204+
}
205+
```
206+
> *from `saveProfile` method of `controller/ProfileController.java`*
207+
208+
This object can be modified according to business requirements and then saved directly into the database using `ProfileRepository::save` method.
209+
Because we used `@GeneratedValue` annotation on `id` field of our java model, Spring Data will automatically generate a document id when it is missing from the request. This allows clients to use `/profile` endpoint both to update existing profiles and create new records.
210+
To achieve this, a client needs to submit a Profile object without the id field.
211+
212+
### Get Profile by Key
213+
Navigate to the `getProfileById` method in [`ProfileController`](https://github.com/couchbase-examples/java-springdata-quickstart/blob/main/src/main/java/trycb/controller/ProfileController.java) class.
214+
This method handles client requests to retrieve a single profile by its unique id.
215+
Sent by the client UUID is passed to the standard `findById` method of `ProfileRepository`, which returns an `Optional` with requested profile:
216+
217+
```java
218+
Profile result = profileRepository.findById(id).orElse(null);
219+
```
220+
> *from getProfileById method of controller/ProfileController.java*
221+
222+
### Search profiles by text
223+
Although Couchbase provides [powerful full-text search capabilities out of the box](https://www.couchbase.com/products/full-text-search), in this demo we use classic `LIKE` query for our profile search endpoint.
224+
Navigate to `listProfiles` method of [Profile Controller](https://github.com/couchbase-examples/java-springdata-quickstart/blob/main/src/main/java/trycb/controller/ProfileController.java).
225+
The endpoint uses customized `findByText` method of [Profile Repository](https://github.com/couchbase-examples/java-springdata-quickstart/blob/main/src/main/java/trycb/repository/ProfileRepository.java):
226+
```java
227+
result = profileRepository.findByText(query, pageRequest).toList();
228+
```
229+
> *from `listProfiles` method in `controller/ProfileController.java`*
230+
231+
The `ProfileRepository::findByQueryMethod` is generated automatically using provided in `@Query` annotation [SpEL](https://docs.spring.io/spring-integration/docs/5.3.0.RELEASE/reference/html/spel.html) template in SQL++:
232+
```java
233+
@Query("#{#n1ql.selectEntity} WHERE firstName LIKE '%' || $1 || '%' OR lastName LIKE '%' || $1 || '%' OR address LIKE '%' || $1 || '%'")
234+
Page<Profile> findByQuery(String query, Pageable pageable);
235+
```
236+
> *definition of `findByQuery` method in `repository/ProfileRepository.java`*
237+
238+
You can find out more about SQL++ in Spring Data in the [connector documentation](https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/#couchbase.repository.querying).
239+
240+
### DELETE Profile
241+
Navigate to the `deleteProfile` method in the [Profile Controller](https://github.com/couchbase-examples/java-springdata-quickstart/blob/main/src/main/java/trycb/controller/ProfileController.java).
242+
We only need the `Key` or id from the user to remove a document using a basic key-value operation.
243+
244+
```java
245+
profileRepository.deleteById(id);
246+
```
247+
248+
> *from `deleteProfile` method of controller/ProfileController.java*
249+
250+
251+
## Conclusion
252+
Setting up a basic REST API in Spring Data with Couchbase is fairly simple. This project, when run with Couchbase Server 7 installed creates a collection in Couchbase, an index for our parameterized [N1QL query](https://docs.couchbase.com/java-sdk/current/howtos/n1ql-queries-with-sdk.html), and showcases basic CRUD operations needed in most applications.

src/main/java/trycb/model/Profile.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public class Profile {
1414
@GeneratedValue
1515
private UUID id;
1616
private String firstName, lastName;
17-
private byte age;
17+
private Byte age;
1818
private String address;
1919

2020
public void setId(UUID id) {

0 commit comments

Comments
 (0)