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
movie
andperson
created
2. Implement the person
class
In this lab, we will learn how to create a Java connection with Cassandra.
Steps
- Open the
04-column
project and navigate to thesrc/main/java
- Create a class called
Person
in theexpert.os.labs.persistence
package -
Define the class with the
@Entity
annotation, specifying the entity name as "Person." -
Add the fields as specified in the provided code, including the
@Id
annotation for theid
field and@Column
annotations for the other fields. -
Implement constructors, getter methods,
equals
,hashCode
, andtoString
methods
Expected results
- Entity
Person
created
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
PersonBuilder
in theexpert.os.labs.persistence
package -
Add the same fields we had previously added to the
Person
class, without the annotations -
Add the builder methods for each field
- Add the
build()
method creating a new instance ofPerson
using its constructor - In the
Person
class 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
AppCassandraOperations
in theexpert.os.labs.persistence
package -
Add a main method
-
Set up a try-with-resources block, inside the
main
method, to manage the Jakarta EESeContainer
that is responsible for dependency injection and managing resources -
Create two user instances using the builder of the
Person
class with different data inside thetry
statementPerson 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
ColumnTemplate
using 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
user2
data based on itsid
and printout the result- use the method
select()
from thetemplate
field - the first parameter is the class and the second is the
id
value
- use the method
-
Add a wait time, then retrieve and printout the same user again
-
Retrieve the
user1
data based on itsid
and printout the result -
Define a private constructor for the
AppCassandraOperations
class 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
user2
does 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
AppCassandraSpecialization
in theexpert.os.labs.persistence
package -
Add a main method
-
Set up a try-with-resources block, inside the
main
method, to manage the Jakarta EESeContainer
that is responsible for dependency injection and managing resources -
Create a user instance using the builder of the
Person
class with some data inside thetry
statement -
Obtain an instance of
CassandraTemplate
using thecontainer.select()
method -
Save the
user1
person entity to Cassandra using a specified consistency level: -
Retrieve data from Cassandra using a CQL query and collect the results into a list of
Person
objects: -
Print the retrieved entities:
-
Define a private constructor for the
AppCassandraSpecialization
class 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() {
}
}