120 minutes (Live workshop) 50 minutes (self-paced), Intermediate, Start Building
This sample is a fully reactive version of the Spring PetClinic application using Spring WebFlux
- Objectives
- Frequently asked questions
- Materials for the Session
- Create your Database
- Create your Schema
- Create your Token
- Start Gitpod and setup your application
- Working with Cassandra Drivers
- Working with Spring Data
- Working with Spring WebFlux
- Working with Angular UI
- Homeworks
- Learn how Apache Cassandra data modelling is different from relational
- Understand how Java Applications connect to Apache Cassandraβ’
- Learn about Spring and Spring Boot Microservices
- Understand what are the benefits of Reactive Programming
- Get a working full stack application Spring Boot-Data-Reactive including a Node.js application for populating data
1οΈβ£ Can I run this workshop on my computer?
There is nothing preventing you from running the workshop on your own machine, If you do so, you will need the following
- git installed on your local system
- JDK 8+ installed on your local system
- Maven 3.6+ installed on your local system
- Node 15 and npm 7 or later
2οΈβ£ What other prerequisites are required?
- You will need a GitHub account
- You will also need an Astra account: don't worry, we'll work through that in the following
3οΈβ£ Do I need to pay for anything for this workshop?
No. All tools and services we provide here are FREE.
4οΈβ£ Will I get a certificate if I attend this workshop?
Attending the session is not enough. You need to complete the homeworks detailed below and you will get a nice badge.
It doesn't matter if you join our workshop live or you prefer to work at your own pace, we have you covered. In this repository, you'll find everything you need for this workshop:
ASTRA DB
is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. No credit card required, $25.00 USD credit every month, roughly 20M read/write operations, 80GB storage monthly - sufficient to run small production workloads.
If you do have an account yet register and sign In to Astra DB this is FREE and NO CREDIT CARD asked. https://astra.datastax.com: You can use your Github
, Google
accounts or register with an email
.
Make sure to chose a password with minimum 8 characters, containing upper and lowercase letters, at least one number and special character
Follow this guide, to set up a pay as you go database with a free $25 monthly credit. You will find below recommended values to enter:
-
For the database name -
workshops
-
For the keyspace name -
spring_petclinic
You can technically use whatever you want and update the code to reflect the keyspace. This is really to get you on a happy path for the first run.
-
For provider and region: Choose a provider (GCP, Azure or AWS) and then the related region is where your database will reside physically (choose one close to you or your users).
-
Create the database. Review all the fields to make sure they are as shown, and click the
Create Database
button.
You will see your new database pending
in the Dashboard.
The status will change to Active
when the database is ready, this will only take 2-3 minutes. You will also receive an email when it is ready.
ποΈ Walkthrough
The Walkthrough mentions the wrong keyspace, make sure to use spring_petclinic
As seen in the slides on the contrary of relational you start with the request and data model BEFORE CODING.
Let's start with the CQL console for the database as whown below.
Next, let's create the tables. In the CQL console use the command:
use spring_petclinic;
Following is the data model we are looking for with 6 tables.
Execute the following in the cql console
DROP INDEX IF EXISTS petclinic_idx_vetname;
DROP INDEX IF EXISTS petclinic_idx_ownername;
DROP TABLE IF EXISTS petclinic_vet;
DROP TABLE IF EXISTS petclinic_vet_by_specialty;
DROP TABLE IF EXISTS petclinic_reference_lists;
DROP TABLE IF EXISTS petclinic_owner;
DROP TABLE IF EXISTS petclinic_pet_by_owner;
DROP TABLE IF EXISTS petclinic_visit_by_pet;
CREATE TABLE IF NOT EXISTS petclinic_vet (
id uuid,
first_name text,
last_name text,
specialties set<text>,
PRIMARY KEY ((id))
);
CREATE TABLE IF NOT EXISTS petclinic_vet_by_specialty (
specialty text,
vet_id uuid,
first_name text,
last_name text,
PRIMARY KEY ((specialty), vet_id)
);
CREATE TABLE IF NOT EXISTS petclinic_owner (
id uuid,
first_name text,
last_name text,
address text,
city text,
telephone text,
PRIMARY KEY ((id))
);
CREATE TABLE IF NOT EXISTS petclinic_pet_by_owner (
owner_id uuid,
pet_id uuid,
pet_type text,
name text,
birth_date date,
PRIMARY KEY ((owner_id), pet_id)
);
CREATE TABLE IF NOT EXISTS petclinic_visit_by_pet (
pet_id uuid,
visit_id uuid,
visit_date date,
description text,
PRIMARY KEY ((pet_id), visit_id)
);
CREATE TABLE IF NOT EXISTS petclinic_reference_lists (
list_name text,
values set<text>,
PRIMARY KEY ((list_name))
);
/** We could search veterinarians by their names. */
CREATE INDEX IF NOT EXISTS petclinic_idx_ownername ON petclinic_owner(last_name);
/** We could search vet by their names. */
CREATE INDEX IF NOT EXISTS petclinic_idx_vetname ON petclinic_vet(last_name);
describe tables;
INSERT INTO petclinic_reference_lists(list_name, values)
VALUES ('pet_type ', {'bird', 'cat', 'dog', 'lizard','hamster','snake'});
INSERT INTO petclinic_reference_lists(list_name, values)
VALUES ('vet_specialty', {'radiology', 'dentistry', 'surgery'});
To connect to the database from Java code we need some credentials, this is what we are going to do here.
Following the Manage Application Tokens docs create a token with Database Admnistrator
roles.
-
Go the
Organization Settings
-
Go to
Token Management
-
Pick the role
Database Admnistrator
on the select box -
Click Generate token
ποΈ Walkthrough
This is what the token page looks like. You can now download the values as a CSV. We will need those values but you can also keep this window open for use later.
Notice the clipboard icon at the end of each value.
-
clientId:
We will use it as a username to contact to the Cassandra database -
clientSecret:
We will use it as a password to contact to the Cassandra database -
appToken:
We will use it as a api token Key to interact with APIs.
To know more about roles of each token you can have a look to this video.
Note: Make sure you don't close the window accidentally or otherwise - if you close this window before you copy the values, the application token is lost forever. They won't be available later for security reasons.
We are now set with the database and credentials. Let's start coding with Spring !
Click the button below it should route you to your workspace: https://gitpod.io/#https://github.com/datastaxdevs/workshop-spring-reactive.
When you first launch gitpod, it builds the image.
This is the home Screen. It is a VSCode instance in the cloud. As you can see, notice multiple panels are open with 2 terminals, the readme and the explorer.
You can open a new terminal from the menu in the ellipsis in the top left hand corner. Then use the Switch terminal panel to move from one to another.
- π All tools are installed
Gitpod provides everything you need to work with JAVA, Node.JS (but also python, docker and many more). Open a new TERMINAL and enter the following command.
Some browsers might block the CTRL+C and CRTL+V if that happen you can paste with right-click and paste.
The first time you paste something in Gitpod your might have a pop-up telling you to accept the command
- Check Java Version
java --version
π₯οΈ Expected output
Picked up JAVA_TOOL_OPTIONS: -Xmx2576m
openjdk 11.0.11 2021-04-20 LTS
OpenJDK Runtime Environment Zulu11.48+21-CA (build 11.0.11+9-LTS)
OpenJDK 64-Bit Server VM Zulu11.48+21-CA (build 11.0.11+9-LTS, mixed mode)
- Check Maven version
mvn -v
π₯οΈ Expected output
Picked up JAVA_TOOL_OPTIONS: -Xmx2576m
Apache Maven 3.8.1 (05c21c65bdfed0f71a2f2ada8b84da59348c4c5d)
Maven home: /home/gitpod/.sdkman/candidates/maven/current
Java version: 11.0.11, vendor: Azul Systems, Inc., runtime: /home/gitpod/.sdkman/candidates/java/11.0.11.fx-zulu
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "5.4.0-1051-gke", arch: "amd64", family: "unix"
- Check Node version
node -v
π₯οΈ Expected output
v14.17.0
- Check NPM version
npm -v
π₯οΈ Expected output
6.14.13
- π Remote explorer
In the tutorial we will also work with the preview and the remote explorer. To switch from source explorer to remote explorer click on dekstop icon on the menu bar in the left (6th item from top).
- π Simple Browser preview
As of now nothing IS running but if you want to open a preview or a new browser use the icons as shown below.
Locate the File application.yaml
in the folder src/main/resources
, there your 3 properties that need to be updated marked with CHANGE_ME
.
- Open the file
gp open /workspace/workshop-spring-reactive/src/main/resources/application.yml
- Edit the lines with the
CHANGE_ME
to include the database properties.
# Enforce listening on port 9966
server:
port: 9966
# Setup your application
astra:
application-token: <CHANGE_ME>
database-id: <CHANGE_ME>
database-region: <CHANGE_ME>
keyspace: spring_petclinic
metrics:
enabled: false
-
The Database region (and keyspace) are located in the details page
-
Make sure the Token looks something like (with AstraCS: preceeding
AstraCS:xxxxxxxxxxx:yyyyyyyyyyy
Take a look at the code of Test01_Connectivity
here we use the CqlSession
and AstraClient
to show some infromation regarding your Astra DB. Execute the test with:
cd /workspace/workshop-spring-reactive
mvn test -Dtest=com.datastax.workshop.petclinic.Test01_Connectivity
π₯οΈ Expected output
== CQL_SESSION ==
+ Your Keyspace: spring_petclinic
+ Vet Specialty:
[dentistry, radiology, surgery]
== ASTRA ==
+ Your OrganizationID: f9460f14-9879-4ebe-83f2-48d3f3dce13c
+ Your Databases:
workshops : id=3ed83de7-d97f-4fb6-bf9f-82e9f7eafa23, region=eu-west-1
The integration to Cassandra is always implemented through the CqlSession
. A first way to implement a DAO is to just use this object explicitly. Check the code at Test02_DaoWithCqlSession
and run the following test:
mvn test -Dtest=com.datastax.workshop.petclinic.Test02_DaoWithCqlSession
- Notice how you needed to put a terminal call
block()
on line 21 or the program is not started.
Project Reactor is a fourth-generation reactive library, based on the Reactive Streams specification, for building non-blocking applications on the JVM. We are using the library reactor-test introducing StepVerifier
to ease the coding of unit tests:
@Test
public void should_list_vet_specialies() {
System.out.println(referenceListDao
.findReferenceList("vet_specialty").block());
StepVerifier
.create(referenceListDao.findReferenceList("vet_specialty"))
.expectNext(Set.of("dentistry", "radiology", "surgery"))
.expectComplete()
.verify();
}
π₯οΈ Expected output
15:34:33.926 INFO com.datastax.workshop.petclinic.Test02_DaoWithCqlSession : Started Test02_DaoWithCqlSession in 14.782 seconds (JVM running for 16.99)
[dentistry, radiology, surgery]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 16.42 s - in com.datastax.workshop.petclinic.Test02_DaoWithCqlSession
We will illustrate this with the Vet
in this package
-
Define an
@Entity
where object attributes matches the table columnsVetEntity
-
Define an
@Dao
interface with only the method you want to implementsVetReactiveDao
@Dao
public interface VetReactiveDao {
@Select
MappedReactiveResultSet<VetEntity> findById(@NotNull UUID vetId);
// More methods....
}
- Define the
@Mapper
to explain how to create theDao
from the the Cqlsession.VetReactiveDaoMapper
@Mapper
public interface VetReactiveDaoMapper {
@DaoFactory
VetReactiveDao vetDao(@DaoKeyspace CqlIdentifier keyspace);
}
Now execute the test to work with the DAO Test03_DaoWithDriverObjectMapping
mvn test -Dtest=com.datastax.workshop.petclinic.Test03_DaoWithDriverObjectMapping
Spring Data provides a common abstraction on top of multiple databases leveraging JPA. The quantity of code is greatly reduced by working with interfaces CrudRepository
and entities.
-
Define an entity
VetEntitySpring
where object attributes matches the table columns. You can notice that the set of annotations is not the same as with java driver mapper. -
Define an interface extending the
ReactiveCassandraRepository
namedVetRepositorySpring
@Repository
public interface VetRepositorySpring
extends ReactiveCassandraRepository<VetEntitySpring, UUID> {
}
Execute the test to work with the DAO Test04_DaoWithSpringData
mvn test -Dtest=com.datastax.workshop.petclinic.Test04_DaoWithSpringData
The original web framework included in the Spring Framework, Spring Web MVC, was purpose-built for the Servlet API and Servlet containers. The reactive-stack web framework, Spring WebFlux, was added later in version 5.0. It is fully non-blocking, supports Reactive Streams back pressure, and runs on such servers as Netty, Undertow, and Servlet 3.1+ containers.
Both web frameworks mirror the names of their source modules (spring-webmvc and spring-webflux) and co-exist side by side in the Spring Framework. Each module is optional. Applications can use one or the other module or, in some cases, bothβββfor example, Spring MVC controllers with the reactive WebClient.
The different DAO we created is injected into a Rest controller. (same as Spring WEB)
@RestController
@RequestMapping("/petclinic/api/specialties")
public class VetSpecialtyController {
@Autowired
ReferenceListReactiveDao dao;
@GetMapping(produces = APPLICATION_JSON_VALUE)
public Mono<ResponseEntity<Set<VetSpecialty>>> getAllVetsSpecialties() {
return refDao.findReferenceList("vet_specialty")
.map(Set::stream)
.map(s -> s.map(VetSpecialty::new)
.collect(Collectors.toSet()))
.map(ResponseEntity::ok);
}
}
mvn test -Dtest=com.datastax.workshop.petclinic.Test05_ApiController
We also used SpringDOC to generate a Swagger UI interface. For more information check the class ApiDocumentationConfig
@RestController
@RequestMapping("/petclinic/api/specialties")
@Api(value="/petclinic/api/specialties", tags = {"Veterinarian Specialties Api"})
public class VetSpecialtyController {
//...
}
You can now go ahead and start the application. The application is listening on port 9966
as defined in application.yaml
(Please do not change this, this is what the user interface is looking for)
Start the application;
mvn spring-boot:run
Open your browser on port 9966
using the the remote explorer or entering in a new terminal.
gp preview "$(gp url 9966)"
You should find the Nice user interface:
Locate the resource Veterinarian Specialties Api
and method the specialities endpoint below to test the service.
GET β/petclinicβ/apiβ/specialties
To execute the service expand the method, locate the button [TRY IT OUT]
Click [Execute]
, this particular method does not take any argument.
You should see a response something like below.
Keep the application running on the first terminal. We need our backend. Let'us start the user interface.
On the terminal spring-petclinic-angular:npm
navigate to the Angular application.
chunk {main} main.js, main.js.map (main) 331 kB [initial] [rendered]
chunk {polyfills} polyfills.js, polyfills.js.map (polyfills) 293 kB [initial] [rendered]
chunk {polyfills-es5} polyfills-es5.js, polyfills-es5.js.map (polyfills-es5) 463 kB [initial] [rendered]
chunk {runtime} runtime.js, runtime.js.map (runtime) 6.08 kB [entry] [rendered]
chunk {scripts} scripts.js, scripts.js.map (scripts) 411 kB [entry] [rendered]
chunk {styles} styles.js, styles.js.map (styles) 1.16 MB [initial] [rendered]
chunk {vendor} vendor.js, vendor.js.map (vendor) 6.87 MB [initial] [rendered]
** Angular Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200/ **
βΉ ο½’wdmο½£: Compiled successfully.
- Kill the running application (the frontend must start after the backend here)
CTRL+C
- Start the application again
cd /workspace/workshop-spring-reactive/spring-petclinic-angular
npm run start
Open your browser on port 4200
using the the remote explorer or entering in a new terminal.
gp preview "$(gp url 4200)"
This is it for the Hands-on TODAY. The angular project is a separate project on its own and we simply reuse it as a submodule but did not code anything there.
Don't forget to complete your assignment and get your verified skill badge! Finish and submit your homework!
-
Complete the practice steps as described below until you have your own app running in Gitpod.
-
Answer the technical questions in the form (We promise, it is NOT difficult if you follow the workshop).
-
Add a funny PET TYPE in the DATABASE and take a SCREENSHOT of the running app with your data.
-
Submit your homework here
-
(totally optional) Challenge for πΆοΈπΆοΈπΆοΈ EXTRA SPICE πΆοΈπΆοΈπΆοΈ. Fork the project, change the code for more repositories to use Spring Data (replacing the Java drivers) and do a pull request. π If the submission has a good quality we will ship a SWAG BOX for free.
Congratulations you made it to the END.