This project extends the Esfinge Querybuilder framework whith an
integration for the NoSQL database Apache Cassandra. Cassandra is an open source NoSQL distributed database
trusted by thousands of companies for scalability and high availability without compromising performance
- Prerequisites
- How to configure the project
- How to use the Querybuilder
- Framework limitations
- Author
- Notes
- Java 11 or higher.
- Maven
- Open the project in
IntelliJ - Load
Maven scriptif prompted by the IDE - Run the tests with
mvn test. Please note that there is another project in this repository calledQueryBuilder_Cassandra_IntegrationTests, which contains the integration tests performed with a packaged version ofQueryBuilder Cassandra.
For an overall view of the Esfinge Querybuilder utilization you can view the
general documentation on the framework. The following is a specific guide for using the
Cassandra plugin along with the framework.
Please keep in mind that for this configuration guide we suppose that you will be using Maven, but you are free to use
any other method to manage dependencies.
- Create an empty project with the following structure:
└───src
└───main
├───java
└───resources
└───META-INF
└───services- In the
META-INF/services/folder you need to create the following three files:- ef.qb.cassandra.CassandraSessionProvider
- ef.qb.cassandra.entity.CassandraEntity
- In the
META-INF/folder you can also add an optionalconfig.jsonfile, see framework limitations for more information. - Now you need to provide the concrete implementations of these two intefaces, namely:
CassandraSessionProvider,CassandraEntity. For example forCassandraSessionProvider:
public class MySessionProvider implements CassandraSessionProvider {
@Override
public Session getSession() {
// your implementation...
return null;
}
}And for CassandraEntity:
@Table(keyspace = "test", name = "person",
readConsistency = "QUORUM",
writeConsistency = "QUORUM",
caseSensitiveKeyspace = false,
caseSensitiveTable = false)
public class Person implements CassandraEntity {
@PartitionKey
private Integer id;
private String name;
// getters and setters omitted...
}For the classes implementing the CassandraEntity please keep in mind that you have to use the @Table
and @PartitionKey annotations of the com.datastax.driver.mapping.annotations library, which is used by this
framework to do the object mapping.
- In the files contained in the
META-INF/servicesfolder you need to include the full path to your implementation of the specific interface, for example in theef.qb.cassandra.CassandraSessionProvider, supposing you named the implementation asMySessionProviderin the packageorg.exampleyou will include the pathorg.example.MySessionProvider. - Create an interface that extends the
net.sf.esfinge.querybuilder.Repositoryinterface, by parametrizing it with an entity class, which should implement theCassandraEntityinterface, for example:
public interface PersonDao extends Repository<Person> {
List<Person> getPerson();
Person getPersonById(Integer id);
}Please keep in mind that you are not forced to implement new custom methods like in the previous example, the
basic CRUD operations, for example save and delete are available by default.
- Here is how to create the instance of the class that actually implements this interface:
PersonDao dao=QueryBuilder.create(PersonDao.class);- Now you are ready to go and the framework should already work. For example you could call the
method
dao.getPersonById(1)and the framework will manage connecting to the database, translating the method into a query and retrieving the results as aPersonclass. - Please keep in mind that the database does not automatically create the
Keyspaceand theTablesfor storing the data, this is left to the user, which can also use theKeyspaceRepositoryclass of the framework, which provides utility methods for handling keyspaces and tables.
If you are using an entity with a custom attribute, you need to use the datastax @UDT annotation
when defining that custom attribute, for example for:
@Table(keyspace = "test", name = "person",
readConsistency = "QUORUM",
writeConsistency = "QUORUM",
caseSensitiveKeyspace = false,
caseSensitiveTable = false)
public class Person implements CassandraEntity {
@PartitionKey
private Integer id;
private String name;
private Address address; // CUSTOM ATTRIBUTE
// getters and setters omitted...
}You need to define the Address class by providing the annotation:
@UDT(keyspace = "test", name = "address")
public class Address implements CassandraEntity {
private String city;
private String state;
// getters and setters omitted...
}In addition, when you are defining the table in the cassandra database,
you need first to create the type and to provide the frozen attribute for
the custom type in the table.
A possible definition of table and type for the
classes above would be:
CREATE TYPE test.address (city text, state text);And for the table:
CREATE TABLE test.Person
(
id int PRIMARY KEY,
name text,
address frozen<address>
);- You need to add the following dependencies either in your
pom.xmlfile or as local.jarfiles, here is a list of theMavendependencies needed:
<dependencies>
<dependency>
<groupId>io.github.esfinge-framework</groupId>
<artifactId>querybuilder-core</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.github.esfinge-framework</groupId>
<artifactId>querybuilder-cassandra</artifactId>
<version>2.3.2</version>
<scope>compile</scope>
</dependency>
</dependencies>An example of an application configured for using the QueryBuilder Cassandra module, can be found in this GitHub repository. The repository contains two versions of the same Java Servlet CRUD apllication, one using QueryBuilder JDBC as persistence layer, the other using QueryBuilder Cassandra.
The cassandra database works similar to a relational database, but since its main purpose is to handle huge amounts of data and provide reliability and scaling mechanisms there are some differences in which operations are allowed or not. The following are the main limitations of cassandra with respect to a relational database:
- Join Queries are not supported
- Queries with
ORare not supported - Ordering is not supported
LIKE,STARTS,ENDSandCONTAINSoperators are not supported.- Comparing to null is not supported
All these limitations have been overcome by the framework by adding a layer at the application level. Please keep in mind that joins are only limited to a depth of 1, for this reason a custom attribute must not contain other custom attributes itself.
In the META-INF/ folder you can also add an optional config.json file, where you can configure the maximum number of
entities that can be ordered from a result and the maximum depth of secondary queries. Secondary queries are used to mimic
the behaviour of the OR operator, this limit basically defines the maximum number of OR connectors in the query, here is an
example fo the configuration file:
{
"orderingLimit": 10,
"secondaryQueryLimit": 2
}If this file is omitted, the configuration will automatically default to:
{
"orderingLimit": 1000,
"secondaryQueryLimit": 3
}Name: Samuel Dalvai
Email: [email protected]
(Refactoring)
Name: Fernando Pereira
Email: [email protected]
This project has been developed at the Free University of Bolzano, as argument for the final thesis
of a BSc in Computer Science, with the supervision of Professor Martins Guerra Eduardo, which is also the author of
the Esfinge Querybuilde Framework. Please visit Esfinge Querybuilder
for more information on the framework.