Apache Cassandra With Java: Introduction to UDT

Apache Cassandra User-defined types (UDTs) can attach multiple data fields, each named and typed, to a single column. The fields used to create a UDT may be any valid data type, including collections and other existing UDTs. Once created, UDTs may be used to define a column in a table. In this post, we'll explore how to use Cassandra's feature with Java.


In simple words, UDT is a type where you can get as much information as you can; for example, give e-commerce in the column family product, there is the option to create a UDT Money where the data are the currency with the value.

SQL
 




x


 
1
CREATE TYPE commerce.money (
2
  currency text,
3
  amount decimal
4
);



It fits perfectly in the OOP model; specifically, we need to create a type or a DDD aggregate in the entity model.

Before, go further in detail on Apache Cassandra UDT, it essential to highlight that Cassandra is not a relational database. Thus, denormalization is your friend, and Cassandra does not have the support to left join.The model should follow the query-driven modeling instead of the normalization.

This tutorial will create a Company entity to explore the UDT feature. This Company entity has five fields:

Cassandra is not schemeless. Thus, it is natural to create the structure before using this. So, the script below is a CQL to create the types, moneyand headquarter, then the Company as column family.

SQL
 




x


 
1
CREATE TYPE IF NOT EXISTS developers.money (currency text, amount decimal);
2
CREATE TYPE IF NOT EXISTS developers.headquarter (city text, country text);
3
CREATE COLUMNFAMILY IF NOT EXISTS developers.Company (name text PRIMARY KEY, cost FROZEN<money>, languages set<text>, contacts map<text, text>, headquarters set<FROZEN<headquarter>>);



In the Cassandra integration with Java, this tutorial uses Jakarta NoSQL once it is a standard to Jakarta EE specification. 

The UDT creation isn't different from a normal entity; therefore, it will use the Entity and Column annotations, similar to JPA. 

Java
 




x


1
import jakarta.nosql.mapping.Column;
2
import jakarta.nosql.mapping.Entity;
3
 
          
4
import java.util.Objects;
5
 
          
6
@Entity
7
public class Headquarter {
8
 
          
9
    @Column
10
    private String city;
11
 
          
12
    @Column
13
    private String country;
14
   //...
15
}
16
 
          



The Money types have two fields one represents the currency and the amount. Java has a specific kind to describes coin, the Currency class. Once Cassandra does not support it, we'll create a convert to move this class data to String.

Java
 




xxxxxxxxxx
1
27


 
1
 
          
2
@Entity
3
public class Money {
4
 
          
5
    @Column
6
    @Convert(MoneyConverter.class)
7
    private Currency currency;
8
 
          
9
    @Column
10
    private BigDecimal amount;
11
  //...
12
}
13
 
          
14
public class MoneyConverter implements AttributeConverter<Currency, String> {
15
 
          
16
    @Override
17
    public String convertToDatabaseColumn(Currency attribute) {
18
        return attribute.getCurrencyCode();
19
    }
20
 
          
21
    @Override
22
    public Currency convertToEntityAttribute(String dbData) {
23
        return Currency.getAvailableCurrencies().stream()
24
                .filter(c -> dbData.equals(c.getCurrencyCode()))
25
                .findAny().orElse(null);
26
    }
27
}
28
 
          




The types of models are ready to use in the Company class. This entity will have trivial annotations such as Entity, Id, and Column. Furthermore, there is an annotation to identify whose that entity is a UDT. The UDT annotation requires an attribute to define the type name.

Java
 




xxxxxxxxxx
1
20


 
1
@Entity
2
public class Company {
3
 
          
4
    @Id("name")
5
    private String name;
6
 
          
7
    @Column
8
    @UDT("money")
9
    private Money cost;
10
 
          
11
    @Column
12
    private Set<String> languages;
13
 
          
14
    @Column
15
    private Map<String, String> contacts;
16
 
          
17
    @Column
18
    @UDT("headquarter")
19
    private Set<Headquarter> headquarters;
20
    //...
21
}



The Company class is ready to use. The next step is storage and retrieves information using the CassandraTemplate. As with any service, make sure that there is a Cassandra instance running. The easiest way is with docker with the command below:

Shell
 




xxxxxxxxxx
1


 
1
docker run -d --name casandra-instance -p 9042:9042 cassandra


Java
 




x


 
1
public class App5 {
2
 
          
3
 
          
4
    public static void main(String[] args) {
5
 
          
6
        try (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
7
 
          
8
            CassandraTemplate template = container.select(CassandraTemplate.class).get();
9
            Currency currency = Currency.getInstance(Locale.US);
10
 
          
11
            Company company = Company.builder()
12
                    .withName("SouJava")
13
                    .addLanguage("Portuguese")
14
                    .addLanguage("English")
15
                    .addLanguage("Italian")
16
                    .addLanguage("Spanish")
17
                    .addHeadquarter(Headquarter.of("Salvador", "Brazil"))
18
                    .addHeadquarter(Headquarter.of("Sao Paulo", "Brazil"))
19
                    .addHeadquarter(Headquarter.of("Leiria", "Portugal"))
20
                    .add("twitter", "otaviojava")
21
                    .add("linkedin", "otaviojava")
22
                    .withCost(Money.of(currency, BigDecimal.valueOf(10_000)))
23
                    .build();
24
 
          
25
            template.insert(company);
26
            Optional<Company> soujava = template.find(Company.class, "SouJava");
27
 
          
28
            System.out.println("the company is " + soujava);
29
 
          
30
 
          
31
        }
32
    }
33
 
          
34
    private App5() {
35
    }
36
}



In this tutorial, we talked about Cassandra's UDT feature and how to use it as a collection or a single field with Jakarta NoSQL.

Source: https://github.com/JNOSQL/demos/tree/master/artemis-demo-java-se/cassandra


 

 

 

 

Top