|
This version is still in development and is not considered stable yet. For the latest stable version, please use Spring Data Cassandra 5.0.1! |
Ahead of Time Optimizations
This chapter covers Spring Data’s Ahead of Time (AOT) optimizations that build upon Spring’s Ahead of Time Optimizations.
Best Practices
Annotate your Domain Types
During application startup, Spring scans the classpath for domain classes for early processing of entities.
By annotating your domain types with Spring Data-specific @Table, @Document or @Entity annotations you can aid initial entity scanning and ensure that those types are registered with ManagedTypes for Runtime Hints.
Classpath scanning is not possible in native image arrangements and so Spring has to use ManagedTypes for the initial entity set.
Runtime Hints
Running an application as a native image requires additional information compared to a regular JVM runtime. Spring Data contributes Runtime Hints during AOT processing for native image usage. These are in particular hints for:
-
Auditing
-
ManagedTypesto capture the outcome of class-path scans -
Repositories
-
Reflection hints for entities, return types, and Spring Data annotations
-
Repository fragments
-
Querydsl
Qclasses -
Kotlin Coroutine support
-
-
Web support (Jackson Hints for
PagedModel)
Ahead of Time Repositories
AOT Repositories are an extension to AOT processing by pre-generating eligible query method implementations. Query methods are opaque to developers regarding their underlying queries being executed in a query method call. AOT repositories contribute query method implementations based on derived, annotated, and named queries that are known at build-time. This optimization moves query method processing from runtime to build-time, which can lead to a significant performance improvement as query methods do not need to be analyzed reflectively upon each application start.
The resulting AOT repository fragment follows the naming scheme of <Repository FQCN>Impl__Aot and is placed in the same package as the repository interface.
You can find all queries in their String form for generated repository query methods.
| Consider AOT repository classes an internal optimization. Do not use them directly in your code as generation and implementation details may change in future releases. |
Running with AOT Repositories
AOT is a mandatory step to transform a Spring application to a native executable, so it is automatically enabled when running in this mode.
When AOT is enabled (either for native compilation or by setting spring.aot.enabled=true), AOT repositories are automatically enabled by default.
You can disable AOT repository generation entirely or only disable Cassandra AOT repositories:
-
Set the
spring.aot.repositories.enabled=falseproperty to disable generated repositories for all Spring Data modules. -
Set the
spring.aot.cassandra.repositories.enabled=falseproperty to disable only C AOT repositories.
AOT repositories contribute configuration changes to the actual repository bean registration to register the generated repository fragment.
| When AOT optimizations are included, some decisions that have been taken at build-time are hard-coded in the application setup. For instance, profiles that have been enabled at build-time are automatically enabled at runtime as well. Also, the Spring Data module implementing a repository is fixed. Changing the implementation requires AOT re-processing. |
Eligible Methods
AOT repositories filter methods that are eligible for AOT processing. These are typically all query methods that are not backed by an implementation fragment.
Supported Features
-
Derived query methods,
@Queryand named query methods. -
Window,Slice,Stream, andOptionalreturn types -
Sort query rewriting
-
Interface and DTO Projections
-
Value Expressions (Those require a bit of reflective information. Mind that using Value Expressions requires expression parsing and contextual information to evaluate the expression)
Limitation
-
Vector Search not yet supported
Excluded methods
-
CrudRepositoryand other base interface methods as their implementation is provided by the base class respective fragments -
Vector Search Methods
Repository Metadata
AOT processing introspects query methods and collects metadata about repository queries. Spring Data Cassandra stores this metadata in JSON files that are named like the repository interface and stored next to it (i.e. within the same package). Repository JSON Metadata contains details about queries and fragments. An example for the following repository is shown below:
interface UserRepository extends CrudRepository<User, Integer> {
List<User> findUserNoArgumentsBy(); (1)
Slice<User> findSliceOfUsersByLastnameStartingWith(String lastname, Pageable page); (2)
@Query("select * from User where emailAddress = :emailAddress")
User findAnnotatedQueryByEmailAddress(String emailAddress); (3)
User findByEmailAddress(String emailAddress); (4)
}
| 1 | Derived query without arguments. |
| 2 | Derived query using pagination. |
| 3 | Annotated query. |
| 4 | Named query. |
{
"name": "com.acme.UserRepository",
"module": "Cassandra",
"type": "IMPERATIVE",
"methods": [
{
"name": "findUserNoArgumentsBy",
"signature": "public abstract java.util.List<com.acme.User> com.acme.UserRepository.findUserNoArgumentsBy()",
"query": {
"query": "select * from user"
}
},
{
"name": "findSliceOfUsersByLastnameStartingWith",
"signature": "public abstract org.springframework.data.domain.Slice<com.acme.User> com.acme.UserRepository.findSliceOfUsersByLastnameStartingWith(java.lang.String,org.springframework.data.domain.Pageable)",
"query": {
"query": "select * from user where lastname like ?"
}
},
{
"name": "findAnnotatedQueryByEmailAddress",
"signature": "public abstract com.acme.User com.acme.UserRepository.findAnnotatedQueryByEmailAddress(java.lang.String)",
"query": {
"query": "select * from user where emailaddress = ?"
}
},
{
"name": "findByEmailAddress",
"signature": "public abstract com.acme.User com.acme.UserRepository.findByEmailAddress(java.lang.String)",
"query": {
"name": "User.findByEmailAddress",
"query": "select * from user where emailaddress = ?"
}
},
{
"name": "count",
"signature": "public abstract long org.springframework.data.repository.CrudRepository.count()",
"fragment": {
"fragment": "org.springframework.data.cassandra.repository.support.SimpleCassandraRepository"
}
}
]
}
Queries may contain the following fields:
-
query: Query descriptor if the method is a query method.-
querythe query used to obtain the query method results. -
name: Name of the named query if the query is a named one.
-
-
fragment: Target fragment if the method call is delegated to a store (repository base class, functional fragment such as Querydsl) or user fragment. Fragments are either described with justfragmentif there is no further interface or asinterfaceandfragmenttuple in case there is an interface (such as Querydsl or user-declared fragment interface).
|
Normalized Query Form
Static analysis of queries allows only a limited representation of runtime query behavior. Queries are represented in their normalized (pre-parsed and rewritten) form:
|