Cassandra - Lab 2
1. The Cassandra structure
In this lab session, we will explore Apache Cassandra by executing various commands to create a keyspace, define column families, and create a secondary index.
Steps
-
Execute the following command in the CQL shell to create a keyspace named "developers" with a replication factor of 3
-
Execute the following commands in the CQL shell to create column families for "Person" and "Movie"
CREATE COLUMNFAMILY IF NOT EXISTS developers.Person (id bigint PRIMARY KEY, name text, contacts map<text, text>); CREATE TYPE IF NOT EXISTS developers.director (name text, movies set<text>); CREATE COLUMNFAMILY IF NOT EXISTS developers.Movie (name text PRIMARY KEY, age int, director FROZEN<director>); -
Create a secondary index on the "age" column of the "Movie" column family using the following command in the CQL shell
-
Use the Cassandra shell to explore, insert, and query data within your instance
Expected results
- Column families
movieandpersoncreated
2. Implement the person class
In this lab, we will learn how to create a Java connection with Cassandra.
Steps
- Open the
04-columnproject and navigate to thesrc/main/java - Create a class called
Personin theexpert.os.labs.persistencepackage -
Define the class with the
@Entityannotation, specifying the entity name as "Person." -
Add the fields as specified in the provided code, including the
@Idannotation for theidfield and@Columnannotations for the other fields. -
Implement constructors, getter methods,
equals,hashCode, andtoStringmethods
Expected results
- Entity
Personcreated
Solution
Click to see...
import jakarta.nosql.Column;
import jakarta.nosql.Entity;
import jakarta.nosql.Id;
import java.util.Map;
@Entity
public class Person {
@Id("id")
private long id;
@Column
private String name;
@Column
private Map<String, String> contacts;
public Person() {
}
public Person(long id, String name, Map<String, String> contacts) {
this.id = id;
this.name = name;
this.contacts = contacts;
}
public long getId() {
return id;
}
public String getName() {
return name;
}
public Map<String, String> getContacts() {
return contacts;
}
}
3. Implement the Builder class
Steps
- Create a class called
PersonBuilderin theexpert.os.labs.persistencepackage -
Add the same fields we had previously added to the
Personclass, without the annotations -
Add the builder methods for each field
- Add the
build()method creating a new instance ofPersonusing its constructor - In the
Personclass add the builder method referring to thePersonBuilder
Solution
Click to see...
import java.util.Map;
public class PersonBuilder {
private long id;
private String name;
private Map<String, String> contacts;
public PersonBuilder id(long id) {
this.id = id;
return this;
}
public PersonBuilder name(String name) {
this.name = name;
return this;
}
public PersonBuilder contacts(Map<String, String> contacts) {
this.contacts = contacts;
return this;
}
public Person build() {
return new Person(id, name, contacts);
}
}
4. Create the execution class for basic Cassandra Operations
Steps
- Create a class called
AppCassandraOperationsin theexpert.os.labs.persistencepackage -
Add a main method
-
Set up a try-with-resources block, inside the
mainmethod, to manage the Jakarta EESeContainerthat is responsible for dependency injection and managing resources -
Create two user instances using the builder of the
Personclass with different data inside thetrystatementPerson user1 = Person.builder() .contacts(Map.of("twitter", "otaviojava", "linkedin", "otaviojava","youtube", "otaviojava")) .name("Otavio Santana").id(1).build(); Person user2 = Person.builder() .contacts(Map.of("twitter", "elderjava", "linkedin", "elderjava","youtube", "elderjava")) .name("Elder Moraes").id(2).build(); -
Obtain an instance of
ColumnTemplateusing Jakarta EE'sSeContainer -
Insert, the two user instances into the column using the
ColumnTemplate, where the second one will have a delay of 1 second, using the methodinsert() -
Retrieve the
user2data based on itsidand printout the result- use the method
select()from thetemplatefield - the first parameter is the class and the second is the
idvalue
- use the method
-
Add a wait time, then retrieve and printout the same user again
-
Retrieve the
user1data based on itsidand printout the result -
Define a private constructor for the
AppCassandraOperationsclass to prevent instantiation since it contains only static methods: -
Run the
main()method
Expected results
-
The following output
Person2 data: Optional[Person{id=2, name='Elder Moraes', contacts={youtube=elderjava, twitter=elderjava, linkedin=elderjava}}] Person2 second retrieve data: Optional.empty Person1 data: Optional[Person{id=1, name='Otavio Santana', contacts={youtube=otaviojava, twitter=otaviojava, linkedin=otaviojava}}] -
The second printout, related to the
user2does not show any data because it expired
Solution
Click to see...
import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import jakarta.nosql.column.ColumnTemplate;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public class AppCassandraOperations {
public static void main(String[] args) throws InterruptedException {
try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
Person user1 = Person.builder()
.contacts(Map.of("twitter", "otaviojava", "linkedin", "otaviojava", "youtube", "otaviojava"))
.name("Otavio Santana").id(1).build();
Person user2 = Person.builder()
.contacts(Map.of("twitter", "elderjava", "linkedin", "elderjava", "youtube", "elderjava"))
.name("Elder Moraes").id(2).build();
ColumnTemplate template = container.select(ColumnTemplate.class).get();
template.insert(user1);
template.insert(user2, Duration.ofSeconds(1));
Optional<Person> person2 = template.find(Person.class, 2L);
System.out.println("Person2 data: " + person2);
TimeUnit.SECONDS.sleep(2L);
person2 = template.find(Person.class, 2L);
System.out.println("Person2 second retrieve data: " + person2);
Optional<Person> person1 = template.find(Person.class, 1L);
System.out.println("Person1 data: " + person1);
}
}
}
5. Create the execution class for Cassandra specialization
In this lab, we will learn how to use specific features with Eclipse JNoSQL.
Steps
- Create a class called
AppCassandraSpecializationin theexpert.os.labs.persistencepackage -
Add a main method
-
Set up a try-with-resources block, inside the
mainmethod, to manage the Jakarta EESeContainerthat is responsible for dependency injection and managing resources -
Create a user instance using the builder of the
Personclass with some data inside thetrystatement -
Obtain an instance of
CassandraTemplateusing thecontainer.select()method -
Save the
user1person entity to Cassandra using a specified consistency level: -
Retrieve data from Cassandra using a CQL query and collect the results into a list of
Personobjects: -
Print the retrieved entities:
-
Define a private constructor for the
AppCassandraSpecializationclass to prevent instantiation since it contains only static methods: -
Run the
main()method
Expected results
- TBD
Solution
Click to see...
import com.datastax.oss.driver.api.core.ConsistencyLevel;
import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import org.eclipse.jnosql.databases.cassandra.mapping.CassandraTemplate;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class AppCassandraSpecialization {
public static void main(String[] args) {
try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
Person user1 = Person.builder().contacts(Map.of("twitter", "ada")).name("Lovelace").id(3).build();
CassandraTemplate template = container.select(CassandraTemplate.class).get();
template.save(user1, ConsistencyLevel.ONE);
List<Person> people = template.<Person>cql("select * from developers.Person where id = 1")
.collect(Collectors.toList());
System.out.println(people);
}
}
private AppCassandraSpecialization() {
}
}