diff --git a/pom.xml b/pom.xml index 6889215f..6a584265 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ Note: when upgrading the Quarkus version or the DataStax Java driver version, make sure that you upgrade them in the quickstart module too. --> - 3.2.4.Final + 3.4.0.CR1 4.17.0 3.23.1 11 diff --git a/quickstart/README.adoc b/quickstart/README.adoc index d1b33288..f1584946 100644 --- a/quickstart/README.adoc +++ b/quickstart/README.adoc @@ -76,6 +76,7 @@ First, let's create our data model – represented by the `Fruit` class – as f [source,java] ---- +@Data @Entity @PropertyStrategy(mutable = false) public class Fruit { @@ -84,12 +85,12 @@ public class Fruit { private final String name; private final String description; + private final Type type; - public Fruit(String name, String description) { - this.name = name; - this.description = description; + public enum Type { + LOCAL, + DELIVERED } - // getters, hashCode, equals, toString methods omitted for brevity } ---- @@ -105,6 +106,29 @@ class name: `fruit`. Also, the `name` field represents a Cassandra partition key, and so we are annotating it with `@PartitionKey` – another annotation from the Object Mapper library. +Also, we use here `@Data` annotation from Lombok that allow us not to write manually getter/setter +and other boilerplate code. And as you can see one field has Enum type that is not supported by +cassandra. To handle it we should register our own `TypeCodec` + +We perform it when app is ready in next way: +[source,java] +---- +@ApplicationScoped +public class AppLifecycleBeanCodecRegistration { + + @Inject CqlSession session; + + void onStart(@Observes StartupEvent ev) { + TypeCodec myEnumCodec = new EnumNameCodec<>(Fruit.Type.class); + + MutableCodecRegistry registry = (MutableCodecRegistry) session.getContext().getCodecRegistry(); + registry.register(myEnumCodec); + } +} +---- +So we subscribe on `StartupEvent` and add to CodecRegistry our codec that +automatically convert Enum to TEXT and vise versa. + IMPORTANT: Entity classes are normally required to have a default no-arg constructor, unless they are annotated with `@PropertyStrategy(mutable = false)`, which is the case here. @@ -174,18 +198,23 @@ The Object Mapper is composed of 2 pieces: Therefore, enabling the Object Mapper requires two steps: -1. Declare the `cassandra-quarkus-mapper-processor` annotation processor. With Maven, this is done -by modifying the compiler plugin configuration in the project's `pom.xml` file as follows: +1. Declare the `lombok` and the `cassandra-quarkus-mapper-processor` annotation processor (order is important). +With Maven, this is done by modifying the compiler plugin configuration in the project's `pom.xml` file as follows: [source,xml] ---- maven-compiler-plugin - 3.10.1 + ${compiler-plugin.version} ${java.version} ${java.version} + + org.projectlombok + lombok + ${lombok.version} + com.datastax.oss.quarkus cassandra-quarkus-mapper-processor @@ -200,6 +229,8 @@ With Gradle, this is done by adding the following line to the `build.gradle` fil [source,groovy] ---- +annotationProcessor "org.projectlombok:lombok:${lombok.version}" + annotationProcessor "com.datastax.oss.quarkus:cassandra-quarkus-mapper-processor:${cassandra-quarkus.version}" ---- @@ -292,11 +323,12 @@ public class FruitResource { } private FruitDto convertToDto(Fruit fruit) { - return new FruitDto(fruit.getName(), fruit.getDescription()); + return new FruitDto(fruit.getName(), fruit.getDescription(), fruit.getType().name()); } private Fruit convertFromDto(FruitDto fruitDto) { - return new Fruit(fruitDto.getName(), fruitDto.getDescription()); + Fruit.Type type = Fruit.Type.valueOf(fruitDto.getType()); + return new Fruit(fruitDto.getName(), fruitDto.getDescription(), type); } } ---- @@ -306,23 +338,18 @@ Notice how `FruitResource` is being injected a `FruitService` instance automatic It is generally not recommended using the same entity object between the REST API and the data access layer. These layers should indeed be decoupled and use distinct APIs in order to allow each API to evolve independently of the other. This is the reason why our REST API is using a different -object: the `FruitDto` class – the word DTO stands for "Data Transfer Object". This DTO object will -be automatically converted to and from JSON in HTTP messages: +object: the `FruitDto` class – the word DTO stands for "Data Transfer Object". It's annotated by +`@Data` that allow us not to write boilerplate code. This DTO object will be automatically +converted to and from JSON in HTTP messages: [source,java] ---- +@Data public class FruitDto { private String name; private String description; - - public FruitDto() {} - - public FruitDto(String name, String description) { - this.name = name; - this.description = description; - } - // getters and setters omitted for brevity + private String type; } ---- diff --git a/quickstart/pom.xml b/quickstart/pom.xml index 5b1c508a..ee53e1b3 100644 --- a/quickstart/pom.xml +++ b/quickstart/pom.xml @@ -27,9 +27,14 @@ 2020 11 - 3.2.4.Final + 3.4.0.CR1 + quarkus-bom + io.quarkus.platform 4.17.0 + 1.18.28 3.23.1 + 3.11.0 + 3.0.0 UTF-8 UTF-8 !native @@ -37,15 +42,16 @@ - io.quarkus - quarkus-bom - ${quarkus.version} + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} pom import com.datastax.oss.quarkus cassandra-quarkus-bom + ${project.version} pom import @@ -90,6 +96,12 @@ io.quarkus quarkus-micrometer-registry-prometheus + + org.projectlombok + lombok + ${lombok.version} + provided + io.quarkus quarkus-junit5 @@ -147,14 +159,20 @@ maven-compiler-plugin - 3.10.1 + ${compiler-plugin.version} ${java.version} ${java.version} + + org.projectlombok + lombok + ${lombok.version} + com.datastax.oss.quarkus cassandra-quarkus-mapper-processor + ${project.version} @@ -162,7 +180,7 @@ maven-surefire-plugin - 3.0.0-M7 + ${surefire-plugin.version} org.jboss.logmanager.LogManager @@ -171,7 +189,7 @@ maven-failsafe-plugin - 3.0.0-M7 + ${surefire-plugin.version} @@ -189,9 +207,9 @@ - io.quarkus + ${quarkus.platform.group-id} quarkus-maven-plugin - ${quarkus.version} + ${quarkus.platform.version} diff --git a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/AppLifecycleBeanCodecRegistration.java b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/AppLifecycleBeanCodecRegistration.java new file mode 100644 index 00000000..b15e14c8 --- /dev/null +++ b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/AppLifecycleBeanCodecRegistration.java @@ -0,0 +1,25 @@ +package com.datastax.oss.quarkus.demo; + +import com.datastax.oss.driver.api.core.CqlSession; +import com.datastax.oss.driver.api.core.type.codec.TypeCodec; +import com.datastax.oss.driver.api.core.type.codec.registry.MutableCodecRegistry; +import com.datastax.oss.driver.internal.core.type.codec.extras.enums.EnumNameCodec; +import io.quarkus.runtime.StartupEvent; +import io.smallrye.config.Priorities; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; + +@ApplicationScoped +public class AppLifecycleBeanCodecRegistration { + + @Inject CqlSession session; + + void onStart(@Observes @Priority(Priorities.PLATFORM - 10) StartupEvent ev) { + TypeCodec myEnumCodec = new EnumNameCodec<>(Fruit.Type.class); + + MutableCodecRegistry registry = (MutableCodecRegistry) session.getContext().getCodecRegistry(); + registry.register(myEnumCodec); + } +} diff --git a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/Fruit.java b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/Fruit.java index 15c67f51..d36e7b73 100644 --- a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/Fruit.java +++ b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/Fruit.java @@ -18,7 +18,7 @@ import com.datastax.oss.driver.api.mapper.annotations.Entity; import com.datastax.oss.driver.api.mapper.annotations.PartitionKey; import com.datastax.oss.driver.api.mapper.annotations.PropertyStrategy; -import java.util.Objects; +import lombok.Data; /** * Represents the name and description of a fruit. @@ -27,6 +27,7 @@ * href="https://docs.datastax.com/en/developer/java-driver/latest/manual/mapper/entities/">Defining * entities with the DataStax Java driver object mapper */ +@Data @Entity @PropertyStrategy(mutable = false) public class Fruit { @@ -34,41 +35,10 @@ public class Fruit { @PartitionKey private final String name; private final String description; + private final Type type; - public Fruit(String name, String description) { - this.name = name; - this.description = description; - } - - /** - * @return The fruit name. - */ - public String getName() { - return name; - } - - /** - * @return The fruit description. - */ - public String getDescription() { - return description; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Fruit that = (Fruit) o; - return Objects.equals(description, that.description) && Objects.equals(name, that.name); - } - - @Override - public int hashCode() { - return Objects.hash(description, name); - } - - @Override - public String toString() { - return String.format("Fruit{name='%s', description='%s'}", name, description); + public enum Type { + LOCAL, + DELIVERED } } diff --git a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/FruitDto.java b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/FruitDto.java index 209c397d..65783c9b 100644 --- a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/FruitDto.java +++ b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/FruitDto.java @@ -15,50 +15,17 @@ */ package com.datastax.oss.quarkus.demo; -import java.util.Objects; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; /** A DTO (Data Transfer Object) used to convey information from a {@link Fruit} domain object. */ +@Data +@NoArgsConstructor +@AllArgsConstructor public class FruitDto { private String name; private String description; - - public FruitDto() {} - - public FruitDto(String name, String description) { - this.name = name; - this.description = description; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - FruitDto fruitDto = (FruitDto) o; - - if (!Objects.equals(name, fruitDto.name)) return false; - return Objects.equals(description, fruitDto.description); - } - - @Override - public int hashCode() { - return Objects.hash(description, name); - } + private String type; } diff --git a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/FruitResource.java b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/FruitResource.java index 8edb8e8b..b6d0ad15 100644 --- a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/FruitResource.java +++ b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/FruitResource.java @@ -47,10 +47,11 @@ public void add(FruitDto fruit) { } private FruitDto convertToDto(Fruit fruit) { - return new FruitDto(fruit.getName(), fruit.getDescription()); + return new FruitDto(fruit.getName(), fruit.getDescription(), fruit.getType().name()); } private Fruit convertFromDto(FruitDto fruitDto) { - return new Fruit(fruitDto.getName(), fruitDto.getDescription()); + Fruit.Type type = Fruit.Type.valueOf(fruitDto.getType()); + return new Fruit(fruitDto.getName(), fruitDto.getDescription(), type); } } diff --git a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/ReactiveFruitResource.java b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/ReactiveFruitResource.java index 8e37fb62..abceb218 100644 --- a/quickstart/src/main/java/com/datastax/oss/quarkus/demo/ReactiveFruitResource.java +++ b/quickstart/src/main/java/com/datastax/oss/quarkus/demo/ReactiveFruitResource.java @@ -47,10 +47,11 @@ public Uni add(FruitDto fruitDto) { } private FruitDto convertToDto(Fruit fruit) { - return new FruitDto(fruit.getName(), fruit.getDescription()); + return new FruitDto(fruit.getName(), fruit.getDescription(), fruit.getType().name()); } private Fruit convertFromDto(FruitDto fruitDto) { - return new Fruit(fruitDto.getName(), fruitDto.getDescription()); + Fruit.Type type = Fruit.Type.valueOf(fruitDto.getType()); + return new Fruit(fruitDto.getName(), fruitDto.getDescription(), type); } } diff --git a/quickstart/src/main/resources/META-INF/resources/fruits.html b/quickstart/src/main/resources/META-INF/resources/fruits.html index 3e250161..ad6a689e 100644 --- a/quickstart/src/main/resources/META-INF/resources/fruits.html +++ b/quickstart/src/main/resources/META-INF/resources/fruits.html @@ -17,7 +17,8 @@ $scope.form = { name: "", - description: "" + description: "", + type: "" }; //Now load the data from server @@ -25,7 +26,7 @@ //HTTP POST methods for add fruits $scope.add = function () { - var data = { "name": $scope.form.name, "description": $scope.form.description }; + var data = { "name": $scope.form.name, "description": $scope.form.description, "type": $scope.form.type }; $http({ method: "POST", @@ -80,17 +81,28 @@

Add a fruit

+
+
+ + +
+

Fruit List

Name
-
Description
+
Description
+
Type
{{ fruit.name }}
-
{{ fruit.description }}
+
{{ fruit.description }}
+
{{ fruit.type }}
diff --git a/quickstart/src/main/resources/init_script.cql b/quickstart/src/main/resources/init_script.cql index 306fca07..18c34cb9 100644 --- a/quickstart/src/main/resources/init_script.cql +++ b/quickstart/src/main/resources/init_script.cql @@ -1,2 +1,2 @@ CREATE KEYSPACE IF NOT EXISTS k1 WITH replication = {'class':'SimpleStrategy', 'replication_factor':1}; -CREATE TABLE IF NOT EXISTS k1.fruit(name text PRIMARY KEY, description text); \ No newline at end of file +CREATE TABLE IF NOT EXISTS k1.fruit(name text PRIMARY KEY, description text, type text); \ No newline at end of file diff --git a/quickstart/src/test/java/com/datastax/oss/demo/FruitResourceIT.java b/quickstart/src/test/java/com/datastax/oss/demo/FruitResourceIT.java index 918930ea..2b970719 100644 --- a/quickstart/src/test/java/com/datastax/oss/demo/FruitResourceIT.java +++ b/quickstart/src/test/java/com/datastax/oss/demo/FruitResourceIT.java @@ -35,7 +35,7 @@ public class FruitResourceIT { @Test public void should_save_and_retrieve_entity() { // given - FruitDto expected = new FruitDto("apple", "this was created via IT test"); + FruitDto expected = new FruitDto("apple", "this was created via IT test", "LOCAL"); // when creating, then given() @@ -63,7 +63,8 @@ public void should_save_and_retrieve_entity() { @Test public void should_save_and_retrieve_entity_reactive() { // given - FruitDto expected = new FruitDto("banana", "this was created via reactive IT test"); + FruitDto expected = + new FruitDto("banana", "this was created via reactive IT test", "DELIVERED"); // when creating, then given()