Quarkus A Kubernetes Native Java stack tailored for OpenJDK HotSpot and GraalVM, crafted from the best of breed Java libraries and standards.
Apache Cassandra is an open source NoSQL distributed database trusted by thousands of companies for scalability and high availability without compromising performance. Linear scalability and proven fault-tolerance on commodity hardware or cloud infrastructure make it the perfect platform for mission-critical data.
CREATING A QUARKUS APPLICATION
Integration Quarkus with Cassandra.
- Configuration Quarkus and Cassandra.
- Define Datas models, Repository and Services.
- Quarkus Resource.
Quarkus Rest CRUD API
Methods | Urls |
---|---|
GET | /products |
GET | /products/{id} |
POST | /products |
PUT | /products/{id} |
DELETE | /products |
Technology
- Java 17
- Quarkus 2.13.3.Final
- Cassandra 4.0.7
- Maven
- Postman
- IntelliJ IDEA
- Docker
Project Structure
Configuration Quarkus Spring Data PostgreSQL
- Quarkus Initializr or Quarkus CLI.
- Maven
pom.xml
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>org.henry</groupId>
<artifactId>quarkus-cassandra-client</artifactId>
<version>1.0.0-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.8.1</compiler-plugin.version>
<maven.compiler.release>17</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<quarkus.platform.artifact-id>quarkus-bom</quarkus.platform.artifact-id>
<quarkus.platform.group-id>io.quarkus.platform</quarkus.platform.group-id>
<quarkus.platform.version>2.13.3.Final</quarkus.platform.version>
<skipITs>true</skipITs>
<surefire-plugin.version>3.0.0-M7</surefire-plugin.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>${quarkus.platform.artifact-id}</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-cassandra-bom</artifactId>
<version>${quarkus.platform.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/io.quarkus/quarkus-resteasy-reactive-jackson -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.oss.quarkus</groupId>
<artifactId>cassandra-quarkus-client</artifactId>
</dependency>
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-mapper-processor</artifactId>
<version>4.15.0</version>
</dependency>
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-mapper-runtime</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>${quarkus.platform.group-id}</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<version>${quarkus.platform.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${compiler-plugin.version}</version>
<configuration>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<source>17</source> <!-- (or higher) -->
<target>17</target> <!-- (or higher) -->
<annotationProcessorPaths>
<path>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-mapper-processor</artifactId>
<version>4.15.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${surefire-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<properties>
<skipITs>false</skipITs>
<quarkus.package.type>native</quarkus.package.type>
</properties>
</profile>
</profiles>
</project>
application.properties: We can configure multiple keyspace.
quarkus.http.port=9000
quarkus.cassandra.contact-points=localhost:9042
quarkus.cassandra.local-datacenter=datacenter1
keyspace.one=inventory
Data class model
package org.henry.model;
import com.datastax.oss.driver.api.mapper.annotations.Entity;
import com.datastax.oss.driver.api.mapper.annotations.PartitionKey;
import java.util.UUID;
@Entity
public class Product {
@PartitionKey private UUID id;
private String description;
public Product() {}
public Product(UUID id, String description) {
this.id = id;
this.description = description;
}
public UUID getId() { return id; }
public void setId(UUID id) { this.id = id; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
}
Repository Interface
package org.henry.repository;
import com.datastax.oss.driver.api.core.PagingIterable;
import com.datastax.oss.driver.api.mapper.annotations.*;
import org.henry.model.Product;
import java.util.UUID;
@Dao
public interface ProductDao {
@Select
Product findById(UUID productId);
@Select
PagingIterable<Product> findAll();
@Update
void update(Product product);
@Insert
void save(Product product);
@Delete
void delete(Product product);
}
Mapper interface
By official documentation: Mapper annotations are used to mark the interface, and indicate what kind of request each method should execute.
Generating the code
The mapper uses annotation processing: it hooks into the Java compiler to analyze annotations, and generate additional classes that implement the mapping logic. Annotation processing is a common technique in modern frameworks, and is generally well supported by build tools and IDEs; this is covered in detail in Configuring the annotation processor.
Pay attention to the compiler output: the mapper processor will sometimes generate warnings if annotations are used incorrectly.
Using the generated code
One of the classes generated during annotation processing is InventoryMapperBuilder
package org.henry.configuration;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.CqlSession;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.henry.mapper.InventoryMapper;
import org.henry.mapper.InventoryMapperBuilder;
import org.henry.repository.ProductDao;
import javax.inject.Singleton;
@Singleton
public class Config {
/**
* Call here multiple keyspace with respective DAO
* */
@ConfigProperty(name = "keyspace.one")
String keyspace1;
public CqlSession session(){
CqlSession session = CqlSession.builder().build();
return session;
}
public ProductDao productDao(){
InventoryMapper inventoryMapper = new InventoryMapperBuilder(session()).build();
ProductDao dao = inventoryMapper.productDao(CqlIdentifier.fromCql(keyspace1));
return dao;
}
}
package org.henry.mapper;
import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.mapper.annotations.DaoFactory;
import com.datastax.oss.driver.api.mapper.annotations.DaoKeyspace;
import com.datastax.oss.driver.api.mapper.annotations.Mapper;
import org.henry.repository.ProductDao;
@Mapper
public interface InventoryMapper {
@DaoFactory
ProductDao productDao(@DaoKeyspace CqlIdentifier keyspace);
}
Service Class
package org.henry.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.henry.configuration.Config;
import org.henry.model.Product;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ApplicationScoped
public class ProductService {
public static JsonMapper mapper = new JsonMapper();
@Inject
Config config;
public void save(Product product) throws JsonProcessingException {
config.productDao().save(new Product(UUID.randomUUID(), product.getDescription()));
}
public void update(Product product, UUID productId) {
Optional<Product> res = Optional.ofNullable(config.productDao().findById(productId));
if (res.isPresent()) {
res.get().setDescription(product.getDescription());
config.productDao().update(res.get());
}
}
public Product findById(UUID productId) {
return config.productDao().findById(productId);
}
public List<Product> getAll() {
return config.productDao().findAll().all();
}
public void delete(Product product) {
config.productDao().delete(product);
}
}
Quarkus Rest APIs Resource
package org.henry.resource;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.henry.model.Product;
import org.henry.service.ProductService;
import javax.inject.Inject;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.List;
import java.util.UUID;
@Path("/products")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class ProductResource {
@Inject
ProductService productService;
@POST
public void save(Product product) throws JsonProcessingException {
productService.save(product);
}
@PUT
@Path("{productId}")
public void update(Product product, UUID productId) {
productService.update(product,productId);
}
@GET
@Path("{productId}")
public Product findById(UUID productId) {
return productService.findById(productId);
}
@GET
public List<Product> getAll() {
return productService.getAll();
}
@DELETE
public void delete(Product product) {
productService.delete(product);
}
}
Set up Cassandra
docker pull cassandra
docker run --name cassandra -p 127.0.0.1:9042:9042 -p 127.0.0.1:9160:9160 -d cassandra
docker exec -it cassandra /bin/bash
#cqlsh
Check datacenter name:
cqlsh> use system;
cqlsh:system> select data_center from local;
data_center
-------------
datacenter1
(1 rows)
cqlsh:system>
Created keyspaces: inventory.
CREATE KEYSPACE inventory
WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 1};
CREATE TABLE inventory.product(id uuid PRIMARY KEY, description text);
Run & Test
Run Quarkus application with next maven command :./mvnw compile quarkus:dev or ./mvnw test (Run test class) by console or IntelliJ.
POST.
http://localhost:9000/products
PUT.
http://localhost:9000/products/{id}
DELETE.
http://localhost:9000/products
GET.
http://localhost:9000/products
GET.
http://localhost:9000/products/{id}
References.
https://quarkus.io/guides/cassandra
https://quarkus.io/guides/cdi-reference
https://quarkus.io/guides/config
https://docs.datastax.com/en/developer/java-driver/4.2/manual/mapper/
https://docs.datastax.com/en/developer/java-driver/4.2/manual/mapper/config/
https://docs.datastax.com/en/developer/java-driver/3.0/
https://cassandra.apache.org/_/index.html
No comments:
Post a Comment