Skip to content

Commit

Permalink
Updates to the Cassandra quickstart guide
Browse files Browse the repository at this point in the history
This commit updates the Cassandra quickstart guide and
clarifies the steps required in order to use the
Cassandra driver Object Mapper framework in a Quarkus
application.

It also makes it simpler to understand which beans
are automatically produced by the Object Mapper.

Finally, it brings some minor enhancements to the
"Eager vs Lazy Initialization" section.

(cherry picked from commit e99e51d)
  • Loading branch information
adutra authored and gsmet committed Dec 20, 2022
1 parent 4636511 commit a85db46
Showing 1 changed file with 107 additions and 21 deletions.
128 changes: 107 additions & 21 deletions docs/src/main/asciidoc/cassandra.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Note also the special return type of the `findAll` method,
link:https://docs.datastax.com/en/drivers/java/latest/com/datastax/oss/driver/api/core/PagingIterable.html[`PagingIterable`]:
it's the base type of result sets returned by the driver.

Finally, let's create the Mapper interface:
Finally, let's create a Mapper interface:

[source,java]
----
Expand All @@ -134,6 +134,89 @@ The `@Mapper` annotation is yet another annotation recognized by the DataStax Ob
mapper is responsible for constructing instances of DAOs – in this case, out mapper is constructing
an instance of our only DAO, `FruitDao`.

Think of the mapper interface as a factory for DAO beans: if you intend to construct and inject a
specific DAO bean in your own code, then you first need to add a `@DaoFactory` method for it in a
`@Mapper` interface.

TIP: `@DaoFactory` method names are irrelevant.

`@DaoFactory` methods should return beans of the following types:

- Any `@Dao`-annotated interface, e.g. `FruitDao`;
- A `CompletationStage` of any `@Dao`-annotated interface, e.g. `CompletionStage<FruitDao>`.
- A `Uni` of any `@Dao`-annotated interface, e.g. `Uni<FruitDao>`.

TIP: `Uni` is a type from the Mutiny library, which is the reactive programming library used by
Quarkus. This will be explained in more detail in the "Reactive Programming" section below.

== Generating the DAO and Mapper Implementations

As you probably guessed already, we are not going to implement the interfaces above. Instead, the
Object Mapper will generate such implementations for us.

The Object Mapper is composed of 2 pieces:

1. A (compile-time) annotation processor that scans the classpath for classes annotated with
`@Mapper`, `@Dao` or `@Entity`, and generates code and CQL queries for them; and
2. A runtime module that contains the logic to execute the generated queries.

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:

[source,xml]
----
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>com.datastax.oss.quarkus</groupId>
<artifactId>cassandra-quarkus-mapper-processor</artifactId>
<version>${cassandra-quarkus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
----

With Gradle, this is done by adding the following line to the `build.gradle` file:

[source,groovy]
----
annotationProcessor "com.datastax.oss.quarkus:cassandra-quarkus-mapper-processor:${cassandra-quarkus.version}"
----

IMPORTANT: Make sure you are enabling the right annotation processor! The Cassandra driver ships
with its Object Mapper annotation processor, called `java-driver-mapper-processor`. But the
Cassandra Quarkus extension also ships with its own annotation processor:
`cassandra-quarkus-mapper-processor`, which has more capabilities than the driver's. This annotation
processor is the only one suitable for use in a Quarkus application, so make sure this is the one
you are using. Also, never use both annotation processors together.

[start=2]
1. Declare the `java-driver-mapper-runtime` dependency in compile scope in the project's `pom.xml`
file as follows:

[source,xml]
----
<dependency>
<groupId>com.datastax.oss</groupId>
<artifactId>java-driver-mapper-runtime</artifactId>
</dependency>
----

IMPORTANT: Although this module is called "runtime", it must be declared in compile scope.

If your project is correctly set up, you should now be able to compile it without errors, and you
should see the generated code in the `target/generated-sources/annotations` directory (if you are
using Maven). You don't need to get familiar with the generated code though, as it is mostly
internal machinery to interact with the database.

== Creating a Service & JSON REST Endpoint

Now let's create a `FruitService` that will be the business layer of our application and store/load
Expand All @@ -157,19 +240,20 @@ public class FruitService {
----

Note how the service is being injected a `FruitDao` instance. This DAO instance is injected
automatically.
automatically, thanks to the generated implementations.

The Cassandra Quarkus extension allows you to inject any of the following beans in your own
components:

- All `@Mapper`-annotated interfaces in your project.
- All `@Dao`-annotated interfaces in your project, as long as they are produced by a corresponding
`@DaoFactory`-annotated method declared in a mapper interface from your project.
- You can also inject a `CompletionStage` or `Uni` of any `@Mapper`-annotated interface.
- Any bean returned by a `@DaoFactory` method (see above for possible bean types).
- The
link:https://javadoc.io/doc/com.datastax.oss.quarkus/cassandra-quarkus-client/latest/com/datastax/oss/quarkus/runtime/api/session/QuarkusCqlSession.html[`QuarkusCqlSession`]
bean: this application-scoped, singleton bean is your main entry point to the Cassandra client; it
is a specialized Cassandra driver session instance with a few methods tailored especially for
Quarkus. Read its javadocs carefully!
- You can also inject `CompletationStage<QuarkusCqlSession>` or `Uni<QuarkusCqlSession>`.

In our example, both `FruitMapper` and `FruitDao` could be injected anywhere. We chose to inject
`FruitDao` in `FruitService`.
Expand Down Expand Up @@ -242,7 +326,7 @@ below snippet to your application's ppm.xml file:
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-jackson</artifactId>
<artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
----

Expand Down Expand Up @@ -725,15 +809,17 @@ You can then point your browser to `http://localhost:8080/fruits.html` and use y

== Eager vs Lazy Initialization

This extension allows you to inject either:
As explained above, this extension allows you to inject many types of beans:

- a `QuarkusCqlSession` bean;
- or the asynchronous version of this bean, that is, `CompletionStage<QuarkusCqlSession>`;
- or the reactive version of this bean, that is, `Uni<QuarkusCqlSession>`.
- A simple bean like `QuarkusCqlSession` or `FruitDao`;
- The asynchronous version of that bean, e.g. `CompletionStage<QuarkusCqlSession>` or
`CompletionStage<FruitDao>;
- The reactive version of that bean, e.g., `Uni<QuarkusCqlSession>` or `Uni<FruitDao>`.

The most straightforward approach is obviously to inject `QuarkusCqlSession` directly. This should
work just fine for most applications; however, the `QuarkusCqlSession` bean needs to be initialized
before it can be used, and this process is blocking.
The most straightforward approach is obviously to inject the bean directly. This should work just
fine for most applications; however, the `QuarkusCqlSession` bean, and all DAO beans that depend on
it, might take some time to initialize before they can be used for the first time, and this process
is blocking.

Fortunately, it is possible to control when the initialization should happen: the
`quarkus.cassandra.init.eager-init` parameter determines if the `QuarkusCqlSession` bean should be
Expand All @@ -744,21 +830,21 @@ that needs to interact with the Cassandra database.

Using lazy initialization speeds up your application startup time, and avoids startup failures if
the Cassandra database is not available. However, it could also prove dangerous if your code is
fully asynchronous, e.g. if you are using https://quarkus.io/guides/reactive-routes[reactive
fully non-blocking, e.g. if you are using https://quarkus.io/guides/reactive-routes[reactive
routes]: indeed, the lazy initialization could accidentally happen on a thread that is not allowed
to block, such as a Vert.x event loop thread. Therefore, setting `quarkus.cassandra.init.eager-init`
to `false` and injecting `QuarkusCqlSession` should be avoided in these contexts.

If you want to use Vert.x (or any other reactive framework) and keep the lazy initialization
behavior, you should instead inject only `CompletionStage<QuarkusCqlSession>` or
`Uni<QuarkusCqlSession>`. When injecting these beans, the initialization process will be triggered
lazily, but it will happen in the background, in a non-blocking way, leveraging the Vert.x event
loop. This way you don't risk blocking the Vert.x thread.
If you want to use Vert.x (or any other non-blocking framework) and keep the lazy initialization
behavior, you should instead inject only a `CompletionStage` or a `Uni` of the desired bean. When
injecting these beans, the initialization process will be triggered lazily, but it will happen in
the background, in a non-blocking way, leveraging the Vert.x event loop. This way you don't risk
blocking the Vert.x thread.

Alternatively, you can set `quarkus.cassandra.init.eager-init` to true: in this case the session
bean will be initialized eagerly during application startup, on the Quarkus main thread. This would
eliminate any risk of blocking a Vert.x thread, at the cost of making your startup time (much)
longer.
bean and all DAO beans will be initialized eagerly during application startup, on the Quarkus main
thread. This would eliminate any risk of blocking a Vert.x thread, at the cost of making your
startup time (much) longer.

== Conclusion

Expand Down

0 comments on commit a85db46

Please sign in to comment.