dk.cloudcreate.essentials.components.document_db.DocumentDbRepository.kt Maven / Gradle / Ivy
/*
* Copyright 2021-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dk.cloudcreate.essentials.components.document_db
import dk.cloudcreate.essentials.components.document_db.annotations.DocumentEntity
import dk.cloudcreate.essentials.components.document_db.annotations.Id
import dk.cloudcreate.essentials.components.document_db.postgresql.*
import dk.cloudcreate.essentials.components.foundation.json.JSONSerializer
import dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil
import dk.cloudcreate.essentials.components.foundation.transaction.jdbi.HandleAwareUnitOfWork
import dk.cloudcreate.essentials.components.foundation.transaction.jdbi.HandleAwareUnitOfWorkFactory
import dk.cloudcreate.essentials.components.foundation.types.RandomIdGenerator
import dk.cloudcreate.essentials.kotlin.types.StringValueType
import org.jdbi.v3.core.Jdbi
import kotlin.reflect.KClass
/**
* Common interface for a Repository that can persist an [ENTITY] in the underlying
* database as a JSON Document
*
* Use the [DocumentDbRepositoryFactory] to create an instance of the [DocumentDbRepository]
* for a given [ENTITY] type:
*
* ```
* @DocumentEntity("orders")
* data class Order(
* @Id
* val orderId: OrderId,
* var description: String,
* var amount: Amount,
* var additionalProperty: Int,
* var orderDate: LocalDateTime,
* @Indexed
* var personName: String,
* var invoiceAddress: Address,
* var contactDetails: ContactDetails,
* override var version: Version = Version.NOT_SAVED_YET,
* override var lastUpdated: OffsetDateTime = OffsetDateTime.now(UTC)
* ) : VersionedEntity
*
* val repositoryFactory: DocumentDbRepositoryFactory = getDocumentDbRepositoryFactory()
* val orderRepository: DocumentDbRepository = repositoryFactory.create(Order::class)
* ```
*
* ## Security
* To support customization of which PostgreSQL table each entity type is stored you can provide your own `tableName` through the [DocumentEntity.tableName] annotation.
*
* The [DocumentEntity.tableName]` and `all the names of the properties in your entity classes` will be directly used in constructing SQL statements through string concatenation.
* This can potentially expose components, such as [PostgresqlDocumentDbRepository], to SQL injection attacks.
*
* **It is the responsibility of the user of this component to sanitize both the [DocumentEntity.tableName] and `all Entity property names` to ensure the security of all the SQL statements generated
* by this component.**
*
* The [PostgresqlDocumentDbRepository] instance, e.g. created by [DocumentDbRepositoryFactory.create],
* will through [EntityConfiguration.configureEntity] call the [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] method to validate the table name
* and Entity property names as a first line of defense.
*
* The [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] provides an initial layer of defense against SQL injection by applying naming conventions intended to
* reduce the risk of malicious input.
* **However, Essentials components as well as [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] does not offer exhaustive protection,
* nor does it assure the complete security of the resulting SQL against SQL injection threats.**
* > The responsibility for implementing protective measures against SQL Injection lies exclusively with the users/developers using the Essentials components and its supporting classes.
* > Users must ensure thorough sanitization and validation of API input parameters, column, table, and index names.
*
* **Insufficient attention to these practices may leave the application vulnerable to SQL injection, potentially endangering the security and integrity of the database.**
*
* It is highly recommended that the [DocumentEntity.tableName] and `all the Entity property names` are only derived from controlled and trusted sources.
*
* To mitigate the risk of SQL injection attacks, external or untrusted inputs should never directly provide the `tableName` or entity property names.
*
* **See also [VersionedEntity]'s security warning.***
*
* @see VersionedEntity
* @see DocumentDbRepositoryFactory
* @see PostgresqlDocumentDbRepository
*/
interface DocumentDbRepository, ID> {
/**
* Add an index to the repository to speedup queries of properties of concrete [VersionedEntity] classes
*
* Example:
* ```
* @DocumentEntity("orders")
* data class Order(
* @Id
* val orderId: OrderId,
* var description: String,
* var amount: Amount,
* var additionalProperty: Int,
* var orderDate: LocalDateTime,
* @Indexed
* var personName: String,
* var invoiceAddress: Address,
* var contactDetails: ContactDetails,
* override var version: Version = Version.NOT_SAVED_YET,
* override var lastUpdated: OffsetDateTime = OffsetDateTime.now(UTC)
* ) : VersionedEntity
*
* orderRepository.addIndex(Index(name="city", listOf(Order::contactDetails then ContactDetails::address then Address::city)))
* orderRepository.addIndex(Index(name="orderdate_amount", listOf(Order::orderDate.asProperty(), Order::amount.asProperty())))
* ```
*
* which, following the pattern: `idx_${tableName}_$indexName` as lower case, will add two indexes to the `orders` table:
* - `idx_orders_city`
* - `idx_orders_orderdate_amount`
*
* ## Security
* The [Index.name] and `all the names of the properties in your entity classes` specified in [Index.properties] will be directly used in constructing SQL statements through string concatenation.
* This can potentially expose components, such as [PostgresqlDocumentDbRepository], to SQL injection attacks.
*
* **It is the responsibility of the user of this component to sanitize both the [Index.name] and `all the names of the properties in your entity classes` specified in [Index.properties]
* to ensure the security of all the SQL statements generated by this component.**
*
* The [PostgresqlDocumentDbRepository.addIndex] will call the [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] method to validate the index name
* as a first line of defense.
*
* The [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] provides an initial layer of defense against SQL injection by applying naming conventions
* intended to reduce the risk of malicious input.
*
* **However, Essentials components as well as [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] does not offer exhaustive protection,
* nor does it assure the complete security of the resulting SQL against SQL injection threats.**
* > The responsibility for implementing protective measures against SQL Injection lies exclusively with the users/developers using the Essentials components and its supporting classes.
* > Users must ensure thorough sanitization and validation of API input parameters, column, table, and index names.
*
* **Insufficient attention to these practices may leave the application vulnerable to SQL injection, potentially endangering the security and integrity of the database.**
*
* It is highly recommended that the [Index.name] and `all the names of the properties in your entity classes` specified in [Index.properties] are only derived from controlled and trusted sources.
*
* To mitigate the risk of SQL injection attacks, external or untrusted inputs should never directly provide the [Index.name] nor any of `the names of the properties in your entity classes` specified in [Index.properties]
*
* **See also [VersionedEntity]'s security warning.***
*
* @param index The index to add, which specifies the properties to include in the index.
* @return this [DocumentDbRepository] to allow chaining of method calls
*/
fun addIndex(index: Index): DocumentDbRepository
/**
* Remove an index from the repository.
* @param index The index to remove
* @return this [DocumentDbRepository] to allow chaining of method calls
*/
fun removeIndex(index: Index): DocumentDbRepository {
return removeIndex(index.name)
}
/**
* Remove an index from the repository.
* @param index The name of the index to remove
* @return this [DocumentDbRepository] to allow chaining of method calls
*/
fun removeIndex(indexName: String): DocumentDbRepository
/**
* Save a new entity instance
* @param entity The entity to persist. If the entity [Id] annotated property is null and of type [String] or type [StringValueType],
* then a random String id value will be assigned using the [RandomIdGenerator]
* @param initialVersion The initial value for the [VersionedEntity.version] - default is [Version.ZERO]
* @return the [entity] provided with the [VersionedEntity.version] and [VersionedEntity.lastUpdated] properties updated
* @throws OptimisticLockingException in case the entity already exists in the database
*/
fun save(entity: ENTITY, initialVersion: Version = Version.ZERO): ENTITY
/**
* Update an existing entity (i.e. an entity that has previously been saved using [save])
* @param entity The entity to save. The [VersionedEntity.version] will automatically be incremented
* @return the [entity] provided with the [VersionedEntity.version] and [VersionedEntity.lastUpdated] properties updated
* @throws OptimisticLockingException in case the entity has been updated in the meantime (i.e. the value of
* [VersionedEntity.version] in the database doesn't match the [VersionedEntity.version] of the [entity])
*/
fun update(entity: ENTITY): ENTITY
/**
* Update an existing entity (i.e. an entity that has previously been saved using [save])
* @param entity The entity to save.
* @param nextVersion The new value for [VersionedEntity.version]
* @return the [entity] provided with the [VersionedEntity.version] and [VersionedEntity.lastUpdated] properties updated
* @throws OptimisticLockingException in case the entity has been updated in the meantime (i.e. the value of
* [VersionedEntity.version] in the database doesn't match the [VersionedEntity.version] of the [entity])
*/
fun update(entity: ENTITY, nextVersion: Version): ENTITY
/**
* Find matching entities using the [query] as criteria
*
* Example:
* ```
* val query = repository.queryBuilder()
* .where(repository.condition()
* .matching {
* Order::additionalProperty lt 50
* })
* .orderBy(Order::additionalProperty, QueryBuilder.Order.ASC)
* .limit(200)
* .offset(0)
* .build()
*
* var result = repository.find(query)
* ```
* or
* ```
* val result = orderRepository.queryBuilder()
* .where(orderRepository.condition()
* .matching {
* (Order::personName like "%John%").or(Order::personName like "%Jane%")
* .and(Order::description like "%unique%")
* })
* .find()
* ```
*
* @param queryBuilder The query builder used to build the query
* @return a list of entities matching the query criteria
*/
fun find(queryBuilder: QueryBuilder): List
/**
* Find an entity by its ID
* @param id The ID of the entity to find
* @return the entity with the given ID or null if not found
*/
fun findById(id: ID): ENTITY?
/**
* Get an entity by its ID
* @param id The ID of the entity to get
* @return the entity with the given ID or throws an exception if not found
*/
fun getById(id: ID): ENTITY {
return findById(id)!!
}
/**
* Check if an entity exists by its ID
* @param id The ID of the entity to check
* @return true if the entity exists, false otherwise
*/
fun existsById(id: ID): Boolean
/**
* Save multiple entities
* @param entities The entities to save
* @return a list of the saved entities
*/
fun saveAll(entities: Iterable): List
/**
* Save multiple entities
* @param entities The entities to save
* @return a list of the saved entities
*/
fun saveAll(vararg entities: ENTITY): List {
return saveAll(entities.toList())
}
/**
* Update multiple entities
* @param entities The entities to update
* @return a list of the updated entities
*/
fun updateAll(entities: Iterable): List
/**
* Update multiple entities
* @param entities The entities to update
* @return a list of the updated entities
*/
fun updateAll(vararg entities: ENTITY): List {
return updateAll(entities.toList())
}
/**
* Delete an entity by its ID
* @param id The ID of the entity to delete
*/
fun deleteById(id: ID)
/**
* Delete an entity
* @param entity The entity to delete
*/
fun delete(entity: ENTITY)
/**
* Delete multiple entities
* @param entities The entities to delete
*/
fun deleteAll(entities: Iterable)
/**
* Delete multiple entities
* @param entities The entities to delete
*/
fun deleteAll(vararg entities: ENTITY) {
return deleteAll(entities.toList())
}
/**
* Find all entities
* @return a list of all entities
*/
fun findAll(): List
/**
* Find multiple entities by their IDs
* @param ids The IDs of the entities to find
* @return a list of the entities with the given IDs
*/
fun findAllById(ids: Iterable): List
/**
* Find multiple entities by their IDs
* @param ids The IDs of the entities to find
* @return a list of the entities with the given IDs
*/
fun findAllById(vararg ids: ID): List {
return findAllById(ids.toList())
}
/**
* Count the number of entities
* @return the number of entities
*/
fun count(): Long
/**
* Delete multiple entities by their IDs
* @param ids The IDs of the entities to delete
*/
fun deleteAllById(ids: Iterable)
/**
* Delete multiple entities by their IDs
* @param ids The IDs of the entities to delete
*/
fun deleteAllById(vararg ids: ID) {
return deleteAllById(ids.toList())
}
/**
* Delete all entities
*/
fun deleteAll()
/**
* Get the entity configuration
* @return the entity configuration
*/
fun entityConfiguration(): EntityConfiguration
/**
* Create a [QueryBuilder] instance.
*
* **See [VersionedEntity]'s security warning.***
*
* Example:
* ```
* val result = orderRepository.queryBuilder()
* .where(orderRepository.condition()
* .matching {
* (Order::personName like "%John%").or(Order::personName like "%Jane%")
* .and(Order::description like "%unique%")
* })
* .find()
* ```
* or
* ```
* val query = repository.queryBuilder()
* .where(repository.condition()
* .matching {
* Order::additionalProperty lt 50
* })
* .orderBy(Order::additionalProperty, QueryBuilder.Order.ASC)
* .limit(200)
* .offset(0)
* .build()
*
* val result = repository.find(query)
* ```
*
* ### Security
* The [DocumentEntity.tableName]` and `all the names of the properties in your entity classes` will be directly used in constructing SQL statements through string concatenation.
* This can potentially expose components, such as [PostgresqlDocumentDbRepository], to SQL injection attacks.
*
* **It is the responsibility of the user of this component to sanitize both the [DocumentEntity.tableName] and `all Entity property names` to ensure the security of all the SQL statements generated
* by this component.**
*
* The [PostgresqlDocumentDbRepository] instance, e.g. created by [DocumentDbRepositoryFactory.create],
* will through [EntityConfiguration.configureEntity] call the [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] method to validate the table name
* and Entity property names as a first line of defense.
*
* The [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] provides an initial layer of defense against SQL injection by applying naming conventions intended to
* reduce the risk of malicious input.
* **However, Essentials components as well as [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] does not offer exhaustive protection,
* nor does it assure the complete security of the resulting SQL against SQL injection threats.**
* > The responsibility for implementing protective measures against SQL Injection lies exclusively with the users/developers using the Essentials components and its supporting classes.
* > Users must ensure thorough sanitization and validation of API input parameters, column, table, and index names.
*
* **Insufficient attention to these practices may leave the application vulnerable to SQL injection, potentially endangering the security and integrity of the database.**
*
* It is highly recommended that the [DocumentEntity.tableName] and `all the Entity property names` are only derived from controlled and trusted sources.
*
* To mitigate the risk of SQL injection attacks, external or untrusted inputs should never directly provide the `tableName` or entity property names.
*
* @return a new [QueryBuilder] instance
*/
fun queryBuilder(): QueryBuilder
/**
* Create a [Condition] that's part of a [QueryBuilder]'s [QueryBuilder.where] statement
*
* **See [VersionedEntity]'s security warning.***
*
* Example:
* ```
* val result = orderRepository.queryBuilder()
* .where(orderRepository.condition()
* .matching {
* (Order::personName like "%John%").or(Order::personName like "%Jane%")
* .and(Order::description like "%unique%")
* })
* .find()
* ```
* or
* ```
* val query = repository.queryBuilder()
* .where(repository.condition()
* .matching {
* Order::additionalProperty lt 50
* })
* .orderBy(Order::additionalProperty, QueryBuilder.Order.ASC)
* .limit(200)
* .offset(0)
* .build()
*
* val result = repository.find(query)
* ```
* @return a new [Condition] instance
*/
fun condition(): Condition
}
/**
* The versioning scheme implements optimistic locking, a concurrency control method used to prevent conflicting updates.
*
* Each time an entity is updated, its [VersionedEntity.version] is checked against the [VersionedEntity.version] of the same entity in the database.
*
* If the versions do not match, the operation is aborted, and an [OptimisticLockingException] is thrown.
*
* This approach ensures that updates do not overwrite changes made by other processes.
* Exception thrown if an entity has be changed
*/
class OptimisticLockingException(message: String) : Exception(message)
/**
* Serializer for an [Id] annotated property within a [VersionedEntity],
* where the [Id] property isn't of type [String]/[StringValueType]
*
* Serializes a typed id instance to a String
*
* @param id the typed id
* @return the string version of the id
*/
typealias IdSerializer = (ID) -> String
private val stringIdSerializer: IdSerializer = { it }
private val stringValueTypeIdSerializer: IdSerializer> = { it.value }
/**
* Create a new [DocumentDbRepositoryFactory] which can create [DocumentDbRepository] capable of persisting
* entities as JSON Documents in the underlying database
*
* Here's an example of configuring it standalone - if you're using the `spring-boot-starter-postgresql`/`spring-boot-starter-postgresql-event-store`
* both the `Jdbi`, `UnitOfWorkFactory` and `JSONSerializer` are available as Spring Beans that can be injected.
*
* ```kotlin
* val repositoryFactory = DocumentDbRepositoryFactory(
* jdbi,
* JdbiUnitOfWorkFactory(jdbi),
* JacksonJSONSerializer(
* EssentialsImmutableJacksonModule.createObjectMapper(
* Jdk8Module(),
* JavaTimeModule()
* ).registerKotlinModule()
* )
* )
* ```
*
* @param jdbi The [Jdbi] instance used to connect to the underlying database
* @param unitOfWorkFactory The [HandleAwareUnitOfWorkFactory] used to control transactions against the underlying database
* @param jsonSerializer The [JSONSerializer] used to serialize the entity instance to and from JSON
*/
@Suppress("UNCHECKED_CAST")
class DocumentDbRepositoryFactory(
jdbi: Jdbi,
private val unitOfWorkFactory: HandleAwareUnitOfWorkFactory,
private val jsonSerializer: JSONSerializer
) {
init {
jdbi.registerArgument(VersionArgumentFactory())
jdbi.registerColumnMapper(VersionColumnMapper())
}
/**
* Create a new [DocumentDbRepository] (based on [PostgresqlDocumentDbRepository]) instance for the given [VersionedEntity] [entityClass],
* where the [Id] annotated property is of type [String]
*
* @param entityClass The entity class implementation (e.g. a data class such as an Order, Product, etc.)
* @return The [PostgresqlDocumentDbRepository]
* @see PostgresqlDocumentDbRepository
*/
fun > createForStringId(
entityClass: KClass
): DocumentDbRepository {
return PostgresqlDocumentDbRepository(
unitOfWorkFactory,
entityClass,
jsonSerializer,
stringIdSerializer
)
}
/**
* Create a new [DocumentDbRepository] (based on [PostgresqlDocumentDbRepository]) instance for the given [VersionedEntity] [entityClass],
* where the [Id] annotated property is of type [StringValueType]
*
* @param entityClass The entity class implementation (e.g. a data class such as an Order, Product, etc.)
* @return The [PostgresqlDocumentDbRepository]
* @see PostgresqlDocumentDbRepository
*/
fun , ID : StringValueType> create(
entityClass: KClass
): DocumentDbRepository {
return PostgresqlDocumentDbRepository(
unitOfWorkFactory,
entityClass,
jsonSerializer,
stringValueTypeIdSerializer as IdSerializer
)
}
/**
* Create a new [DocumentDbRepository] (based on [PostgresqlDocumentDbRepository]) instance for the given [entityClass]
* where the [Id] annotated property within the [VersionedEntity] isn't of type [String]/[StringValueType]
*
* @param entityClass The entity class implementation (e.g. a data class such as an Order, Product, etc.)
* @param idSerializer Serializer for an [Id] annotated property within a [VersionedEntity],
* where the [Id] property isn't of type [String]/[StringValueType]
* @return The [PostgresqlDocumentDbRepository]
* @see PostgresqlDocumentDbRepository
*/
fun , ID> createForCompositeId(
entityClass: KClass,
idSerializer: IdSerializer
): DocumentDbRepository {
return PostgresqlDocumentDbRepository(
unitOfWorkFactory,
entityClass,
jsonSerializer,
idSerializer
)
}
}
/**
* Definition of an [Index] being added to [PostgresqlDocumentDbRepository] using [PostgresqlDocumentDbRepository.addIndex]
*
* ## Security
* The [Index.name] and `all the names of the properties in your entity classes` specified in [Index.properties] will be directly used in constructing SQL statements through string concatenation.
* This can potentially expose components, such as [PostgresqlDocumentDbRepository], to SQL injection attacks.
*
* **It is the responsibility of the user of this component to sanitize both the [Index.name] and `all the names of the properties in your entity classes` specified in [Index.properties]
* to ensure the security of all the SQL statements generated by this component.**
*
* The [PostgresqlDocumentDbRepository.addIndex] will call the [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] method to validate the index name
* as a first line of defense.
*
* The [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] provides an initial layer of defense against SQL injection by applying naming conventions
* intended to reduce the risk of malicious input.
*
* **However, Essentials components as well as [dk.cloudcreate.essentials.components.foundation.postgresql.PostgresqlUtil.checkIsValidTableOrColumnName] does not offer exhaustive protection,
* nor does it assure the complete security of the resulting SQL against SQL injection threats.**
* > The responsibility for implementing protective measures against SQL Injection lies exclusively with the users/developers using the Essentials components and its supporting classes.
* > Users must ensure thorough sanitization and validation of API input parameters, column, table, and index names.
*
* **Insufficient attention to these practices may leave the application vulnerable to SQL injection, potentially endangering the security and integrity of the database.**
*
* It is highly recommended that the [Index.name] and `all the names of the properties in your entity classes` specified in [Index.properties] are only derived from controlled and trusted sources.
*
* To mitigate the risk of SQL injection attacks, external or untrusted inputs should never directly provide the [Index.name] nor any of `the names of the properties in your entity classes` specified in [Index.properties]
*
* **See also [VersionedEntity]'s security warning.***
*
* @param name the name of the index - see security note on [PostgresqlDocumentDbRepository.addIndex]
* @param properties the properties that are included in the index - see security note on [PostgresqlDocumentDbRepository.addIndex]
*/
data class Index(
val name: String,
val properties: List>
) {
init {
PostgresqlUtil.checkIsValidTableOrColumnName(name)
require(properties.isNotEmpty()) { "You have to specify at least 1 property" }
properties.forEach { PostgresqlUtil.checkIsValidTableOrColumnName(it.name()) }
}
}
/**
* Base class that can delegate all [DocumentDbRepository] method calls onto a delegate instance of
* [DocumentDbRepository] such as [PostgresqlDocumentDbRepository] (which by design cannot be used as superclass)
*
* The [DelegatingDocumentDbRepository] can be used to create Entity specific repositories with custom query methods
*
* Example `OrderRepository`:
* ```
* class OrderRepository(delegateTo: DocumentDbRepository) : DelegatingDocumentDbRepository(delegateTo) {
* fun findOrdersWithAmountGreaterThan(amount: Amount): List {
* return queryBuilder().where(
* condition().matching {
* Order::amount gt amount
* }
* ).find()
* }
* }
* ```
*
* Initializing the `OrderRepository`:
* ```
* val repositoryFactory = DocumentDbRepositoryFactory(
* jdbi,
* JdbiUnitOfWorkFactory(jdbi),
* JacksonJSONSerializer(
* EssentialsImmutableJacksonModule.createObjectMapper(
* Jdk8Module(),
* JavaTimeModule()
* ).registerKotlinModule()
* )
* )
*
* val orderRepository = OrderRepository(repositoryFactory.create(Order::class))
* ```
* @param delegateTo The [DocumentDbRepository] instance where all [DocumentDbRepository] method calls will be delegated to
*/
open class DelegatingDocumentDbRepository, ID>(val delegateTo: DocumentDbRepository) : DocumentDbRepository {
override fun addIndex(index: Index): DocumentDbRepository = delegateTo.addIndex(index)
override fun removeIndex(indexName: String): DocumentDbRepository = delegateTo.removeIndex(indexName)
override fun deleteAll() = delegateTo.deleteAll()
override fun findAll(): List = delegateTo.findAll()
override fun count(): Long = delegateTo.count()
override fun entityConfiguration(): EntityConfiguration = delegateTo.entityConfiguration()
override fun queryBuilder(): QueryBuilder = delegateTo.queryBuilder()
override fun condition(): Condition = delegateTo.condition()
override fun deleteAllById(ids: Iterable) = delegateTo.deleteAllById(ids)
override fun findAllById(ids: Iterable): List = delegateTo.findAllById(ids)
override fun deleteAll(entities: Iterable) = delegateTo.deleteAll(entities)
override fun delete(entity: ENTITY) = delegateTo.delete(entity)
override fun deleteById(id: ID) = delegateTo.deleteById(id)
override fun updateAll(entities: Iterable): List = delegateTo.updateAll(entities)
override fun saveAll(entities: Iterable): List = delegateTo.saveAll(entities)
override fun existsById(id: ID): Boolean = delegateTo.existsById(id)
override fun findById(id: ID): ENTITY? = delegateTo.findById(id)
override fun find(queryBuilder: QueryBuilder): List = delegateTo.find(queryBuilder)
override fun update(entity: ENTITY, nextVersion: Version): ENTITY = delegateTo.update(entity, nextVersion)
override fun update(entity: ENTITY): ENTITY = delegateTo.update(entity)
override fun save(entity: ENTITY, initialVersion: Version): ENTITY = delegateTo.save(entity, initialVersion)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy