By Dassi Orleando, Alibaba Cloud Tech Share Author. Tech Share is Alibaba Cloud's incentive program to encourage the sharing of technical knowledge and best practices within the cloud community.
ArangoDB is a multi-model NoSQL database management system allowing to build high performance applications using a convenient SQL-like query language or JavaScript extensions.
Previously, we've seen how to install ArangoDB (server and client) into an Alibaba Cloud Elastic Compute Service (ECS) Ubuntu instance and experimented with the command-line and graphical tools to perform some operations on databases, collections and users. Let's consider we've that base to better follow the current write-up which will show a basic API building using ArangoDB as database engine into a Spring-Boot based project.
We'll be building a simple API using Java/Spring resources. Here are some important dependencies
Maven will be used as build automation to configure our project and setup the dependencies.
Let's visit https://start.spring.io to generate the structure of a Spring Boot project with the Web dependency as shown below:
Now we've a fresh Maven project we can unzip and open with our favorite Java IDE.
<dependency>
<groupId>com.arangodb</groupId>
<artifactId>arangodb-spring-data</artifactId>
<version>3.1.0</version>
</dependency>
Spring Data ArangoDB requires at least ArangoDB 3.0 to function.
Note: ArangoDB Java driver can be used in any Java application while arango-db-spring-data is proper for those Spring projects where we want to use repositories.
Let's be sure the pom.xml have almost the following structure and/or content:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>xyz.dassiorleando</groupId>
<artifactId>spring-arangodb</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>spring-arangodb-api</name>
<description>Basic API: Spring Boot and ArangoDB</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
<dependency>
<groupId>com.arangodb</groupId>
<artifactId>arangodb-spring-data</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
ArangoDB's multi-model feature allows to manage many types of database with the same engine, for the sake of this article we will create and use a Document database for a basic CRUD API.
From the dashboard let's create a database called alibabacloudblog as illustrated in the screenshot below (owned by the current user: root):
Let's switch to be connected to the database we have just created (still from the admin panel) so that we'll manage our database to do things like create collections (represents table in relational DBMS).
We then create the article collection, it's a self-explaining word representing an article as in a blog with title and content as main fields.
As example, here's the screen to create the Document collection article:
Let's create our custom database configuration class ArangoDBConfiguration, it's where we'll define our database user credentials to use within the project, the database name as well as the ArangoDB host.
The configuration class is as follow:
/**
* ArangoDB configuration class
* To configure and provide the bean to inject later for database interactions
* @author dassiorleando
*/
@Configuration
@EnableArangoRepositories(basePackages = { "xyz.dassiorleando.arangodb" })
public class ArangoDBConfiguration extends AbstractArangoConfiguration {
/**
* Database url (server + port) & credentials configs
* @return
*/
@Override
public ArangoDB.Builder arango() {
ArangoDB.Builder arango = new ArangoDB.Builder()
.host("127.0.0.1", 8529)
.useProtocol(Protocol.HTTP_JSON)
.user("root")
.password("root");
return arango;
}
/**
* Database name
* @return
*/
@Override
public String database() {
return "alibabacloudblog";
}
}
Another option which is the most used as it's easy to identify in a large project is to use a configuration file located in the resources folder of our project, here's how our arangodb.properties file content should be:
arangodb.hosts=127.0.0.1:8529
arangodb.user=root
arangodb.password=root
Here's how to load this file so that our bean will be configured as stated:
@Override
public ArangoDB.Builder arango() {
InputStream in = ArangoDBConfiguration.class.getResourceAsStream("arangodb.properties");
ArangoDB.Builder arango = new ArangoDB.Builder()
.loadProperties(in);
return arango;
}
Note: When using ArangoDB 3.0 it is required to set the transport protocol to HTTP and fetch the dependency org.apache.httpcomponents:httpclient, here's the maven syntax to use:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.1</version>
</dependency>
Let's show how simple it's to define a model in the following case, here's the Article.java content:
/**
* The Article model
* @author dassiorleando
*/
@Document("article")
public class Article {
@Id
private String id;
@NotNull
private String title;
private String content;
private String author;
public Article() {
}
public String getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Article{" +
"id='" + id + '\'' +
", title='" + title + '\'' +
", content='" + content + '\'' +
", author='" + author + '\'' +
'}';
}
}
Next, we create the Article service. It's actually the place we'll be writing every codes necessary to manage Article while interacting with the correct database collection.
Spring Data provides default implementations for most CRUD operations which Spring Data ArangoDB is built on (we first inject the article repository):
/**
* Article service for basic CRUD operations
* @author dassiorleando
*/
@Service
public class ArticleService {
@Autowired
private ArticleRepository articleRepository;
/**
* To create an article
* @param article
* @return article
*/
public Article save(Article article) {
// Save the article
return articleRepository.save(article);
}
/**
* To update an article
* @param article
* @return the updated article
*/
public Article update(Article article) {
String articleId = article.getId();
if (articleId == null || articleId.isEmpty()) return null;
// Update the article
return articleRepository.save(article);
}
/**
* Find a single article by its id
* @param articleId
* @return article
*/
public Optional<Article> findOne(String articleId) {
if (articleId == null || articleId.isEmpty()) return Optional.empty();
return articleRepository.findById(articleId);
}
/**
* Find all saved articles so far
* @return
*/
public Iterable<Article> findAll() {
return articleRepository.findAll();
}
/**
* Delete a single article by its id
* @param articleId
*/
public void delete(String articleId) {
if (articleId == null || articleId.isEmpty()) return;
articleRepository.deleteById(articleId);
}
}
This service is used as an interface for operations across Article collections and/or further custom business logics, these functions will then be used into the Article controller.
Note that all documents created in any collections will automatically get the following server-generated attributes:
Whenever you run queries on the documents in collections, don't be surprised if these additional attributes are returned as well.
At any moment of your App development you can decide to add more fields to a collection's document by just updating the Article model (an annotated POJO), as the field are dynamic there is no need to perform any other special queries on other documents unless you've something else to accomplish.
You can count the number of documents of a collections using the following query as described in the guide:
FOR doc IN collection
COLLECT WITH COUNT INTO length
RETURN length
A more populated approach to handle the whole data would have been by making a JOIN between two collections, one the Article who serve to define the blog's article model and another the user defining the article's authors. Let's read more about join here.
Instead of using the predefined collections functions we have up here in the ArangoDB Spring Data, one might try writing plain AQL queries and end up with the same results, here's a simple query getting a user by its Name and ID (using bind parameters):
FOR u IN users
FILTER u.id == @id && u.name == @name
RETURN u
Here are some keywords available to use in order to perform AQL queries:
FOR
: array iterationRETURN
: results projectionFILTER
: results filteringSORT
: result sortingLIMIT
: result slicingLET
: variable assignmentCOLLECT
: result groupingINSERT
: insertion of new documentsUPDATE
: (partial) update of existing documentsREPLACE
: replacement of existing documentsREMOVE
: removal of existing documentsUPSERT
: insertion or update of existing documentsWhen it comes to more complex queries queries using ArangoDB Query Language (AQL) can be supplied with the @Query annotation on methods in our repositories and we call the corresponding function as other.
Query example into a repository:
@Query("FOR a IN articles FILTER a.title == @0 RETURN a")
Iterable<Article> getArticleByTitle(String title);
Instead, we can only define the following repository method to perform the same query, but now with Spring Data possibilities:
Iterable<Article> findByTitle(String title);
Here's the ArticleRepository content:
/**
* Article repository
* @author dassiorleando
*/
public interface ArticleRepository extends ArangoRepository<Article, String> {
// Supposing that article is the collection name to perform the query on
@Query("FOR a IN article FILTER a.title == @0 RETURN a")
Iterable<Article> getArticleByTitle(String title);
Iterable<Article> findByTitle(String title);
}
If interested in using it for custom business logic into your API, here is the AQL getting started which is clearly showing details on how to use AQL. Queries guide with more examples are available on the reference manual.
The article controller is following Spring MVC in a rest way, so that it's just dealing with incoming parameter while sending back JSON data as output:
/**
* Article controller for CRUD operations
* @author dassiorleando
*/
@RestController
public class ArticleResource {
private final Logger log = LoggerFactory.getLogger(ArticleResource.class);
private final ArticleService articleService;
public ArticleResource(ArticleService articleService) {
this.articleService = articleService;
}
/**
* To create an article
* @param article
* @return
*/
@PostMapping("/articles")
public Article create(@RequestBody @Valid Article article) {
log.debug("Create an article with the properties {}", article);
return articleService.save(article);
}
/**
* To update an article
* @param article
* @return
*/
@PutMapping("/articles")
public Article update(@RequestBody @Valid Article article) {
log.debug("Update the article of title {} with the properties {}", article.getTitle(), article);
return articleService.update(article);
}
/**
* Get the list of all articles
* @return
*/
@GetMapping("/articles")
public Iterable<Article> list() {
log.debug("We just get the list of articles one more time");
return articleService.findAll();
}
/**
* Find an article by its id
* @param id
* @return
*/
@GetMapping("/articles/{id}")
public Optional<Article> findByTitle(@PathVariable @NotNull String id) {
log.debug("Load the article of id: {}", id);
return articleService.findOne(id);
}
/**
* Delete an article by its title
* @param id
*/
@DeleteMapping("/articles/{id}")
public void deleteById(@PathVariable @NotNull String id) {
log.debug("Delete the article of id: {}", id);
articleService.delete(id);
}
}
A simple command is available to run our API on our ECS or locally on any computer as simply as typing mvn spring-boot:run knowing that you've compiled (mvn clean install) your project before.
When running ArangoDB, you may encounter some issues as discussed in the following link: https://github.com/arangodb/spring-data-demo/issues/7
In this article, we've seen how to build a basic Spring Boot API using ArangoDB as the database management system with its ArangoDB Java Driver wrapped by Spring Data ArangoDB interface.
The full source code for this article can be found on my GitHub page.
2,599 posts | 763 followers
FollowAlibaba Clouder - September 7, 2020
Alibaba Clouder - November 26, 2018
Alibaba Clouder - June 5, 2019
Alibaba Clouder - June 5, 2019
Alibaba Cloud Community - July 31, 2024
Alibaba Cloud Native Community - December 1, 2021
2,599 posts | 763 followers
FollowElastic and secure virtual cloud servers to cater all your cloud hosting needs.
Learn MoreLearn More
An encrypted and secure cloud storage service which stores, processes and accesses massive amounts of data from anywhere in the world
Learn MoreMore Posts by Alibaba Clouder
Dikky Ryan Pratama May 9, 2023 at 5:45 am
if other than arangodb is it still possible?