Data
Storage Interfaces
Quartz Data provides a flexible abstraction layer for declaring and using storage interfaces. These allow you to define repository-style interfaces and access entities through consistent patterns.
Declaring a Storage Interface
To create a storage, annotate an interface with @Storage and extend a base interface such as InMemoryStorage, which already includes basic CRUD, listing, and paging capabilities:
@Storage
public interface EmployeeStorage extends InMemoryStorage<Employee, UUID> {
List<Employee> findByName(String name);
}
Important
All storages must extend an interface annotated with @SuperStorage
, which links the storage to a backend provider.
Quartz will automatically register and inject a proxy-backed implementation of this interface.
Built-in Default: InMemoryStorage
Quartz Data comes with built-in support for InMemoryStorage, backed by a thread-safe ConcurrentHashMap. This is ideal for initial development, testing, or lightweight environments where persistence is not required.
Zero Config
No setup is required, the necessary provider and executor are auto-registered.
Supported Operations & Return Types
Quartz storages support three dynamic operations:
- find → for fetching data
- count → for counting results
- exists → for checking existence
'find' Return Types
The find operation supports multiple return types:
Return Type | Description |
---|---|
List<T> | Default list result |
Set<T> | Converts the result into a Set |
Stream<T> | Returns a Java stream |
Optional<T> | Returns the first result or empty |
Page<T> | Requires a Pagination parameter |
T (Entity class) | Returns the first result or throws if none found |
Pagination Required
Methods returning Page<T>
must include a Pagination
argument.
'count' Return Types
Must return a numeric type:
- long, Long
- Any subclass of
Number
'exists' Return Types
Must return:
- boolean, Boolean
Query Support (Overview)
Quartz supports two query declaration styles:
- Method name-based: findBy, countBy, existsBy, etc.
- Annotation-based with @Query using QQL (Quartz Query Language)
For more details, see:
Discovery & Registration
Quartz automatically detects all @Storage interfaces located in the same package or subpackages of the class annotated with @QuartzPlugin.
To explicitly include storages from other packages:
@Bootstrapper
@DiscoverStorages(basePackages = "com.example.external")
public class ExternalStorageDiscovery {}
Or to register specific ones manually:
@Bootstrapper
@DiscoverStorages({EmployeeStorage.class, ProductStorage.class})
public class ExplicitStorageList {}
You should know
@DiscoverStorages
must be placed on a valid Quartz bean, such as one annotated with @Bootstrapper
, @Injectable
, or directly on your @QuartzPlugin
class.
Under the Hood
- StorageDiscovery scans for @Storage interfaces
- StorageFactory checks the @SuperStorage interface to determine the appropriate StorageProvider
- The provider creates the actual storage implementation and its corresponding QueryExecutor
- A proxy is created using StorageMethodInterceptor
- The proxy is registered via StorageRegistrar and becomes injectable
Extending Quartz with Custom Storages
You can define your own base storage type by creating an interface annotated with @SuperStorage:
@SuperStorage(MyCustomStorageProvider.class)
public interface RedisStorage<E, ID> extends SimpleStorage<E, ID> {}
Then implement a custom StorageProvider:
public class MyCustomStorageProvider implements StorageProvider {
public <E, ID> RedisStorageImpl<E, ID> create(Class<E> entity, Class<ID> id) {
return new RedisStorageImpl<>(entity, id);
}
public <E, ID> QueryExecutor<E> getQueryExecutor(SimpleStorage<E, ID> storage) {
return new RedisQueryExecutor<>(...);
}
}
Finally, register the provider as a bean:
@Provide
public MyCustomStorageProvider redisStorageProvider() {
return new MyCustomStorageProvider();
}
Quartz will then recognize and route any storage that extends RedisStorage<E, ID> to your custom provider.