diff --git a/pom.xml b/pom.xml
index d87478c95..10c44c1c3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -381,6 +381,10 @@
org.apache.maven.pluginsmaven-surefire-plugin
+
+ **/*Test.java
+ **/*Tests.java
+ --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED
diff --git a/src/main/asciidoc/reference/configure-data-settings.adoc b/src/main/asciidoc/reference/configure-data-settings.adoc
index 4fdf6323f..0122b2e2b 100644
--- a/src/main/asciidoc/reference/configure-data-settings.adoc
+++ b/src/main/asciidoc/reference/configure-data-settings.adoc
@@ -23,7 +23,7 @@ class ApplicationConfig extends AbstractAerospikeDataConfiguration {
@Override
protected void configureDataSettings(AerospikeDataSettings.AerospikeDataSettingsBuilder builder) {
builder.createIndexesOnStartup(true);
- builder.indexCacheRefreshFrequencySeconds(3600);
+ builder.indexCacheRefreshSeconds(3600);
builder.queryMaxRecords(10000L);
}
}
@@ -58,12 +58,19 @@ Create secondary indexes specified using `@Indexed` annotation on startup.
*Default*: `true`.
[[configure-data-settings.index-cache-refresh-frequency-seconds]]
-=== indexCacheRefreshFrequencySeconds
+=== indexCacheRefreshSeconds
Automatically refresh indexes cache every seconds.
*Default*: `3600`.
+[[configure-data-settings.server-version-refresh-frequency-seconds]]
+=== serverVersionRefreshSeconds
+
+Automatically refresh cached server version every seconds.
+
+*Default*: `3600`.
+
[[configure-data-settings.query-max-records]]
=== queryMaxRecords
@@ -71,6 +78,13 @@ Limit amount of results returned by server. Non-positive value means no limit.
*Default*: `10 000`.
+[[configure-data-settings.batch-write-size]]
+=== batchWriteSize
+
+Maximum batch size for batch write operations. Non-positive value means no limit.
+
+*Default*: `100`.
+
[[configure-data-settings.keep-original-key-types]]
=== keepOriginalKeyTypes
diff --git a/src/main/java/org/springframework/data/aerospike/config/AbstractAerospikeDataConfiguration.java b/src/main/java/org/springframework/data/aerospike/config/AbstractAerospikeDataConfiguration.java
index 437743342..f16b35393 100644
--- a/src/main/java/org/springframework/data/aerospike/config/AbstractAerospikeDataConfiguration.java
+++ b/src/main/java/org/springframework/data/aerospike/config/AbstractAerospikeDataConfiguration.java
@@ -33,6 +33,7 @@
import org.springframework.data.aerospike.query.cache.IndexRefresher;
import org.springframework.data.aerospike.query.cache.IndexesCacheUpdater;
import org.springframework.data.aerospike.query.cache.InternalIndexOperations;
+import org.springframework.data.aerospike.server.version.ServerVersionSupport;
@Slf4j
@Configuration
@@ -43,9 +44,10 @@ public AerospikeTemplate aerospikeTemplate(IAerospikeClient aerospikeClient,
MappingAerospikeConverter mappingAerospikeConverter,
AerospikeMappingContext aerospikeMappingContext,
AerospikeExceptionTranslator aerospikeExceptionTranslator,
- QueryEngine queryEngine, IndexRefresher indexRefresher) {
+ QueryEngine queryEngine, IndexRefresher indexRefresher,
+ ServerVersionSupport serverVersionSupport) {
return new AerospikeTemplate(aerospikeClient, nameSpace(), mappingAerospikeConverter,
- aerospikeMappingContext, aerospikeExceptionTranslator, queryEngine, indexRefresher);
+ aerospikeMappingContext, aerospikeExceptionTranslator, queryEngine, indexRefresher, serverVersionSupport);
}
@Bean(name = "aerospikeQueryEngine")
@@ -75,22 +77,23 @@ public AerospikePersistenceEntityIndexCreator aerospikePersistenceEntityIndexCre
}
@Bean(name = "aerospikeIndexRefresher")
- public IndexRefresher indexRefresher(IAerospikeClient aerospikeClient, IndexesCacheUpdater indexesCacheUpdater) {
+ public IndexRefresher indexRefresher(IAerospikeClient aerospikeClient, IndexesCacheUpdater indexesCacheUpdater,
+ ServerVersionSupport serverVersionSupport) {
IndexRefresher refresher = new IndexRefresher(aerospikeClient, aerospikeClient.getInfoPolicyDefault(),
- new InternalIndexOperations(new IndexInfoParser()), indexesCacheUpdater);
+ new InternalIndexOperations(new IndexInfoParser()), indexesCacheUpdater, serverVersionSupport);
refresher.refreshIndexes();
- int refreshFrequency = aerospikeDataSettings().getIndexCacheRefreshFrequencySeconds();
+ int refreshFrequency = aerospikeDataSettings().getIndexCacheRefreshSeconds();
processCacheRefreshFrequency(refreshFrequency, refresher);
- log.debug("AerospikeDataSettings.indexCacheRefreshFrequencySeconds: {}", refreshFrequency);
+ log.debug("AerospikeDataSettings.indexCacheRefreshSeconds: {}", refreshFrequency);
return refresher;
}
- private void processCacheRefreshFrequency(int indexCacheRefreshFrequencySeconds, IndexRefresher indexRefresher) {
- if (indexCacheRefreshFrequencySeconds <= 0) {
+ private void processCacheRefreshFrequency(int indexCacheRefreshSeconds, IndexRefresher indexRefresher) {
+ if (indexCacheRefreshSeconds <= 0) {
log.info("Periodic index cache refreshing is not scheduled, interval ({}) is <= 0",
- indexCacheRefreshFrequencySeconds);
+ indexCacheRefreshSeconds);
} else {
- indexRefresher.scheduleRefreshIndexes(indexCacheRefreshFrequencySeconds);
+ indexRefresher.scheduleRefreshIndexes(indexCacheRefreshSeconds);
}
}
}
diff --git a/src/main/java/org/springframework/data/aerospike/config/AbstractReactiveAerospikeDataConfiguration.java b/src/main/java/org/springframework/data/aerospike/config/AbstractReactiveAerospikeDataConfiguration.java
index d0ed25c58..2a0a85df9 100644
--- a/src/main/java/org/springframework/data/aerospike/config/AbstractReactiveAerospikeDataConfiguration.java
+++ b/src/main/java/org/springframework/data/aerospike/config/AbstractReactiveAerospikeDataConfiguration.java
@@ -37,6 +37,7 @@
import org.springframework.data.aerospike.query.cache.IndexesCacheUpdater;
import org.springframework.data.aerospike.query.cache.InternalIndexOperations;
import org.springframework.data.aerospike.query.cache.ReactorIndexRefresher;
+import org.springframework.data.aerospike.server.version.ServerVersionSupport;
/**
* Configuration with beans needed for reactive stuff
@@ -53,10 +54,11 @@ public ReactiveAerospikeTemplate reactiveAerospikeTemplate(MappingAerospikeConve
AerospikeExceptionTranslator aerospikeExceptionTranslator,
IAerospikeReactorClient aerospikeReactorClient,
ReactorQueryEngine reactorQueryEngine,
- ReactorIndexRefresher reactorIndexRefresher) {
+ ReactorIndexRefresher reactorIndexRefresher,
+ ServerVersionSupport serverVersionSupport) {
return new ReactiveAerospikeTemplate(aerospikeReactorClient, nameSpace(), mappingAerospikeConverter,
- aerospikeMappingContext,
- aerospikeExceptionTranslator, reactorQueryEngine, reactorIndexRefresher);
+ aerospikeMappingContext, aerospikeExceptionTranslator, reactorQueryEngine, reactorIndexRefresher,
+ serverVersionSupport);
}
@Bean(name = "reactiveAerospikeQueryEngine")
@@ -76,10 +78,11 @@ public ReactorQueryEngine reactorQueryEngine(IAerospikeReactorClient aerospikeRe
@Bean(name = "reactiveAerospikeIndexRefresher")
public ReactorIndexRefresher reactorIndexRefresher(IAerospikeReactorClient aerospikeReactorClient,
- IndexesCacheUpdater indexesCacheUpdater) {
+ IndexesCacheUpdater indexesCacheUpdater,
+ ServerVersionSupport serverVersionSupport) {
ReactorIndexRefresher refresher = new ReactorIndexRefresher(aerospikeReactorClient,
aerospikeReactorClient.getInfoPolicyDefault(),
- new InternalIndexOperations(new IndexInfoParser()), indexesCacheUpdater);
+ new InternalIndexOperations(new IndexInfoParser()), indexesCacheUpdater, serverVersionSupport);
refresher.refreshIndexes().block();
return refresher;
}
@@ -103,8 +106,7 @@ protected ClientPolicy getClientPolicy() {
public ReactiveAerospikePersistenceEntityIndexCreator aerospikePersistenceEntityIndexCreator(
ObjectProvider aerospikeMappingContext,
AerospikeIndexResolver aerospikeIndexResolver,
- ObjectProvider template)
- {
+ ObjectProvider template) {
boolean indexesOnStartup = aerospikeDataSettings().isCreateIndexesOnStartup();
log.debug("AerospikeDataSettings.indexesOnStartup: {}", indexesOnStartup);
return new ReactiveAerospikePersistenceEntityIndexCreator(aerospikeMappingContext,
diff --git a/src/main/java/org/springframework/data/aerospike/config/AerospikeDataConfigurationSupport.java b/src/main/java/org/springframework/data/aerospike/config/AerospikeDataConfigurationSupport.java
index e0c5b787a..c2709a1ca 100644
--- a/src/main/java/org/springframework/data/aerospike/config/AerospikeDataConfigurationSupport.java
+++ b/src/main/java/org/springframework/data/aerospike/config/AerospikeDataConfigurationSupport.java
@@ -17,6 +17,7 @@
import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Host;
+import com.aerospike.client.IAerospikeClient;
import com.aerospike.client.policy.ClientPolicy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinition;
@@ -37,6 +38,7 @@
import org.springframework.data.aerospike.query.StatementBuilder;
import org.springframework.data.aerospike.query.cache.IndexesCache;
import org.springframework.data.aerospike.query.cache.IndexesCacheHolder;
+import org.springframework.data.aerospike.server.version.ServerVersionSupport;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.mapping.model.FieldNamingStrategy;
import org.springframework.data.mapping.model.PropertyNameFieldNamingStrategy;
@@ -119,6 +121,24 @@ public AerospikeIndexResolver aerospikeIndexResolver() {
return new AerospikeIndexResolver();
}
+ @Bean(name = "aerospikeServerVersionSupport")
+ public ServerVersionSupport serverVersionSupport(IAerospikeClient aerospikeClient) {
+ ServerVersionSupport serverVersionSupport = new ServerVersionSupport(aerospikeClient);
+ int serverVersionRefreshFrequency = aerospikeDataSettings().getServerVersionRefreshSeconds();
+ processServerVersionRefreshFrequency(serverVersionRefreshFrequency, serverVersionSupport);
+ return serverVersionSupport;
+ }
+
+ private void processServerVersionRefreshFrequency(int serverVersionRefreshSeconds,
+ ServerVersionSupport serverVersionSupport) {
+ if (serverVersionRefreshSeconds <= 0) {
+ log.info("Periodic server version refreshing is not scheduled, interval ({}) is <= 0",
+ serverVersionRefreshSeconds);
+ } else {
+ serverVersionSupport.scheduleServerVersionRefresh(serverVersionRefreshSeconds);
+ }
+ }
+
protected Set> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getMappingBasePackage();
Set> initialEntitySet = new HashSet<>();
diff --git a/src/main/java/org/springframework/data/aerospike/config/AerospikeDataSettings.java b/src/main/java/org/springframework/data/aerospike/config/AerospikeDataSettings.java
index 56f183e02..2fa76f6a7 100644
--- a/src/main/java/org/springframework/data/aerospike/config/AerospikeDataSettings.java
+++ b/src/main/java/org/springframework/data/aerospike/config/AerospikeDataSettings.java
@@ -33,10 +33,16 @@ public class AerospikeDataSettings {
boolean createIndexesOnStartup = true;
@Builder.Default
// Automatically refresh indexes cache every seconds
- int indexCacheRefreshFrequencySeconds = 3600;
+ int indexCacheRefreshSeconds = 3600;
+ @Builder.Default
+ // Automatically refresh cached server version every seconds
+ int serverVersionRefreshSeconds = 3600;
@Builder.Default
// Limit amount of results returned by server. Non-positive value means no limit
long queryMaxRecords = 10_000L;
+ @Builder.Default
+ // Maximum batch size for batch write operations
+ int batchWriteSize = 100;
// Define how @Id fields (primary keys) and Map keys are stored: false - always as String,
// true - preserve original type if supported
@Builder.Default
diff --git a/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java b/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java
index 7346f085b..06a45c5ba 100644
--- a/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java
+++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeOperations.java
@@ -50,8 +50,7 @@
public interface AerospikeOperations {
/**
- * Return set name used for the given entityClass in the namespace configured for the AerospikeTemplate in
- * use.
+ * Return set name used for the given entityClass in the namespace configured for the AerospikeTemplate in use.
*
* @param entityClass The class to get the set name for.
* @return The set name used for the given entityClass.
@@ -143,8 +142,8 @@ public interface AerospikeOperations {
void saveAll(Iterable documents);
/**
- * Save multiple documents within the given set (overrides the default set associated with the documents) in one batch request.
- * The policies are analogous to {@link #save(Object)}.
+ * Save multiple documents within the given set (overrides the default set associated with the documents) in one
+ * batch request. The policies are analogous to {@link #save(Object)}.
*
* The order of returned results is preserved. The execution order is NOT preserved.
*
@@ -221,7 +220,8 @@ public interface AerospikeOperations {
void persist(T document, WritePolicy writePolicy);
/**
- * Persist a document within the given set (overrides the default set associated with the document) using specified WritePolicy.
+ * Persist a document within the given set (overrides the default set associated with the document) using specified
+ * WritePolicy.
*
* @param document The document to be persisted. Must not be {@literal null}.
* @param writePolicy The Aerospike write policy for the inner Aerospike put operation. Must not be
@@ -297,8 +297,8 @@ public interface AerospikeOperations {
void updateAll(Iterable documents);
/**
- * Update multiple records within the given set (overrides the default set associated with the
- * documents) in one batch request. The policies are analogous to {@link #update(Object)}.
+ * Update multiple records within the given set (overrides the default set associated with the documents) in one
+ * batch request. The policies are analogous to {@link #update(Object)}.
*
* The order of returned results is preserved. The execution order is NOT preserved.
*
@@ -317,7 +317,7 @@ public interface AerospikeOperations {
* Truncate/Delete all records in the set determined by the given entityClass.
*
* @param entityClass The class to extract set name from. Must not be {@literal null}.
- * @deprecated since 4.6.0, use deleteAll(Class entityClass) instead.
+ * @deprecated since 4.6.0, use {@link AerospikeOperations#deleteAll(Class)} instead.
*/
void delete(Class entityClass);
@@ -327,7 +327,7 @@ public interface AerospikeOperations {
* @param id The id of the record to delete. Must not be {@literal null}.
* @param entityClass The class to extract set name from. Must not be {@literal null}.
* @return whether the document existed on server before deletion.
- * @deprecated since 4.6.0, use deleteById(Object id, Class entityClass) instead.
+ * @deprecated since 4.6.0, use {@link AerospikeOperations#deleteById(Object, Class)} instead.
*/
boolean delete(Object id, Class entityClass);
@@ -367,14 +367,13 @@ public interface AerospikeOperations {
boolean deleteById(Object id, String setName);
/**
- * Delete records by ids using a single batch delete operation, set name will be determined by
- * the given entityClass.
+ * Delete records by ids using a single batch delete operation, set name will be determined by the given
+ * entityClass.
*
* This operation requires Server version 6.0+.
*
* @param ids The ids of the records to be deleted. Must not be {@literal null}.
- * @param entityClass The class to extract set name from. Must not be
- * {@literal null}.
+ * @param entityClass The class to extract set name from. Must not be {@literal null}.
* @throws AerospikeException.BatchRecordArray if batch delete results contain errors
* @throws org.springframework.dao.DataAccessException if batch operation failed (see
* {@link DefaultAerospikeExceptionTranslator} for details)
@@ -394,34 +393,13 @@ public interface AerospikeOperations {
*/
void deleteByIds(Iterable> ids, String setName);
- /**
- * Batch delete records by ids. Set name will be determined by the given entityClass.
- *
- * This operation requires Server version 6.0+.
- *
- * @param ids The ids of the records to be deleted. Must not be {@literal null}.
- * @param entityClass The class to extract set name from. Must not be {@literal null}.
- * @throws AerospikeException.BatchRecordArray if batch delete results contain errors or null records
- */
- void deleteByIds(Collection> ids, Class entityClass);
-
- /**
- * Batch delete records by ids within the given set.
- *
- * This operation requires Server version 6.0+.
- *
- * @param ids The ids of the records to be deleted. Must not be {@literal null}.
- * @param setName Set name to use.
- * @throws AerospikeException.BatchRecordArray if batch delete results contain errors or null records
- */
- void deleteByIds(Collection> ids, String setName);
-
/**
* Perform a single batch delete for records from different sets.
*
* This operation requires Server version 6.0+.
*
- * @param groupedKeys Keys grouped by document type. Must not be {@literal null}.
+ * @param groupedKeys Keys grouped by document type. Must not be {@literal null}, groupedKeys.getEntitiesKeys() must
+ * not be {@literal null}.
* @throws AerospikeException.BatchRecordArray if batch delete results contain errors
* @throws org.springframework.dao.DataAccessException if batch operation failed (see
* {@link DefaultAerospikeExceptionTranslator} for details)
@@ -443,20 +421,20 @@ public interface AerospikeOperations {
void deleteAll(String setName);
/**
- * Find an existing record matching the document's class and id, add map values to the corresponding bins of the record and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's class and id, add map values to the corresponding bins of the
+ * record and return the modified record mapped to the document's class.
*
- * @param document The document to get set name and id from and to map the record to. Must not be
- * {@literal null}.
+ * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}.
* @param values The Map of bin names and values to add. Must not be {@literal null}.
* @return Modified record mapped to the document's class.
*/
T add(T document, Map values);
/**
- * Find an existing record matching the document's id and the given set name, add map values to the corresponding bins of the record and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's id and the given set name, add map values to the corresponding
+ * bins of the record and return the modified record mapped to the document's class.
*
- * @param document The document to get id from and to map the record to. Must not be
- * {@literal null}.
+ * @param document The document to get id from and to map the record to. Must not be {@literal null}.
* @param setName Set name to use.
* @param values The Map of bin names and values to add. Must not be {@literal null}.
* @return Modified record mapped to the document's class.
@@ -464,10 +442,10 @@ public interface AerospikeOperations {
T add(T document, String setName, Map values);
/**
- * Find an existing record matching the document's class and id, add specified value to the record's bin and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's class and id, add specified value to the record's bin and return
+ * the modified record mapped to the document's class.
*
- * @param document The document to get set name and id from and to map the record to. Must not be
- * {@literal null}.
+ * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}.
* @param binName Bin name to use add operation on. Must not be {@literal null}.
* @param value The value to add.
* @return Modified record mapped to the document's class.
@@ -475,10 +453,10 @@ public interface AerospikeOperations {
T add(T document, String binName, long value);
/**
- * Find an existing record matching the document's id and the given set name, add specified value to the record's bin and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's id and the given set name, add specified value to the record's
+ * bin and return the modified record mapped to the document's class.
*
- * @param document The document to get id from and to map the record to. Must not be
- * {@literal null}.
+ * @param document The document to get id from and to map the record to. Must not be {@literal null}.
* @param setName Set name to use.
* @param binName Bin name to use add operation on. Must not be {@literal null}.
* @param value The value to add.
@@ -487,17 +465,18 @@ public interface AerospikeOperations {
T add(T document, String setName, String binName, long value);
/**
- * Find an existing record matching the document's class and id, append map values to the corresponding bins of the record and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's class and id, append map values to the corresponding bins of the
+ * record and return the modified record mapped to the document's class.
*
- * @param document The document to get set name and id from and to map the record to. Must not be
- * {@literal null}.
+ * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}.
* @param values The Map of bin names and values to append. Must not be {@literal null}.
* @return Modified record mapped to the document's class.
*/
T append(T document, Map values);
/**
- * Find an existing record matching the document's id and the given set name, append map values to the corresponding bins of the record and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's id and the given set name, append map values to the corresponding
+ * bins of the record and return the modified record mapped to the document's class.
*
* @param document The document to get id from and to map the record to. Must not be {@literal null}.
* @param setName Set name to use.
@@ -507,10 +486,10 @@ public interface AerospikeOperations {
T append(T document, String setName, Map values);
/**
- * Find an existing record matching the document's class and id, append specified value to the record's bin and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's class and id, append specified value to the record's bin and
+ * return the modified record mapped to the document's class.
*
- * @param document The document to get set name and id from and to map the record to. Must not be
- * {@literal null}.
+ * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}.
* @param binName Bin name to use append operation on.
* @param value The value to append.
* @return Modified record mapped to the document's class.
@@ -518,7 +497,8 @@ public interface AerospikeOperations {
T append(T document, String binName, String value);
/**
- * Find an existing record matching the document's id and the given set name, append specified value to the record's bin and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's id and the given set name, append specified value to the record's
+ * bin and return the modified record mapped to the document's class.
*
* @param document The document to get id from and to map the record to. Must not be {@literal null}.
* @param setName Set name to use.
@@ -529,17 +509,18 @@ public interface AerospikeOperations {
T append(T document, String setName, String binName, String value);
/**
- * Find an existing record matching the document's class and id, prepend map values to the corresponding bins of the record and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's class and id, prepend map values to the corresponding bins of the
+ * record and return the modified record mapped to the document's class.
*
- * @param document The document to get set name and id from and to map the record to. Must not be
- * {@literal null}.
+ * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}.
* @param values The Map of bin names and values to prepend. Must not be {@literal null}.
* @return Modified record mapped to the document's class.
*/
T prepend(T document, Map values);
/**
- * Find an existing record matching the document's id and the given set name, prepend map values to the corresponding bins of the record and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's id and the given set name, prepend map values to the
+ * corresponding bins of the record and return the modified record mapped to the document's class.
*
* @param document The document to get id from and to map the record to. Must not be {@literal null}.
* @param setName Set name to use.
@@ -549,10 +530,10 @@ public interface AerospikeOperations {
T prepend(T document, String setName, Map values);
/**
- * Find an existing record matching the document's class and id, prepend specified value to the record's bin and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's class and id, prepend specified value to the record's bin and
+ * return the modified record mapped to the document's class.
*
- * @param document The document to get set name and id from and to map the record to. Must not be
- * {@literal null}.
+ * @param document The document to get set name and id from and to map the record to. Must not be {@literal null}.
* @param binName Bin name to use prepend operation on.
* @param value The value to prepend.
* @return Modified record mapped to the document's class.
@@ -560,7 +541,8 @@ public interface AerospikeOperations {
T prepend(T document, String binName, String value);
/**
- * Find an existing record matching the document's id and the given set name, prepend specified value to the record's bin and return the modified record mapped to the document's class.
+ * Find an existing record matching the document's id and the given set name, prepend specified value to the
+ * record's bin and return the modified record mapped to the document's class.
*
* @param document The document to get id from and to map the record to. Must not be {@literal null}.
* @param setName Set name to use.
@@ -584,8 +566,7 @@ public interface AerospikeOperations {
* The matching record will be mapped to the given entityClass.
*
* @param id The id of the record to find. Must not be {@literal null}.
- * @param entityClass The class to extract set name from and to map the record to. Must not be
- * {@literal null}.
+ * @param entityClass The class to extract set name from and to map the record to. Must not be {@literal null}.
* @return The document mapped to entityClass's type or null if nothing is found
*/
T findById(Object id, Class entityClass);
@@ -596,8 +577,8 @@ public interface AerospikeOperations {
* The matching record will be mapped to the given entityClass.
*
* @param id The id of the record to find. Must not be {@literal null}.
- * @param entityClass The class to map the record to and to get entity properties from (such as expiration). Must not
- * be {@literal null}.
+ * @param entityClass The class to map the record to and to get entity properties from (such as expiration). Must
+ * not be {@literal null}.
* @param setName Set name to find the document from.
* @return The record mapped to entityClass's type or null if document does not exist
*/
@@ -611,8 +592,7 @@ public interface AerospikeOperations {
* @param id The id of the record to find. Must not be {@literal null}.
* @param entityClass The class to extract set name from. Must not be {@literal null}.
* @param targetClass The class to map the record to. Must not be {@literal null}.
- * @return The record mapped to targetClass's type or null if document doesn't
- * exist.
+ * @return The record mapped to targetClass's type or null if document doesn't exist.
*/
S findById(Object id, Class entityClass, Class targetClass);
@@ -625,22 +605,18 @@ public interface AerospikeOperations {
* @param entityClass The class to get entity properties from (such as expiration). Must not be {@literal null}.
* @param targetClass The class to map the record to. Must not be {@literal null}.
* @param setName Set name to find the document from.
- * @return The record mapped to targetClass's type or null if document doesn't
- * exist.
+ * @return The record mapped to targetClass's type or null if document doesn't exist.
*/
S findById(Object id, Class entityClass, Class targetClass, String setName);
/**
- * Find records by ids using a single batch read operation, set name will be determined by the
- * given entityClass.
+ * Find records by ids using a single batch read operation, set name will be determined by the given entityClass.
*
* The records will be mapped to the given entityClass.
*
* @param ids The ids of the documents to find. Must not be {@literal null}.
- * @param entityClass The class to extract set name from and to map the records to. Must not be
- * {@literal null}.
- * @return The matching records mapped to entityClass's type, if no document
- * exists, an empty list is returned.
+ * @param entityClass The class to extract set name from and to map the records to. Must not be {@literal null}.
+ * @return The matching records mapped to entityClass's type, if no document exists, an empty list is returned.
*/
List findByIds(Iterable> ids, Class entityClass);
@@ -657,8 +633,7 @@ public interface AerospikeOperations {
List findByIds(Iterable> ids, Class entityClass, String setName);
/**
- * Find records by ids using a single batch read operation, set name will be determined by the
- * given entityClass.
+ * Find records by ids using a single batch read operation, set name will be determined by the given entityClass.
*
* The records will be mapped to the given targetClass.
*
@@ -685,10 +660,11 @@ public interface AerospikeOperations {
/**
* Execute a single batch request to find several records, possibly from different sets.
*
- * Aerospike provides functionality to get records from different sets in 1 batch request. This method receives
- * keys grouped by document type as a parameter and returns Aerospike records mapped to documents grouped by type.
+ * Aerospike provides functionality to get records from different sets in 1 batch request. This method receives keys
+ * grouped by document type as a parameter and returns Aerospike records mapped to documents grouped by type.
*
- * @param groupedKeys Must not be {@literal null}.
+ * @param groupedKeys Keys grouped by document type. Must not be {@literal} null, groupedKeys.getEntitiesKeys() must
+ * not be {@literal null}.
* @return grouped documents.
*/
GroupedEntities findByIds(GroupedKeys groupedKeys);
@@ -731,8 +707,8 @@ Object findByIdUsingQuery(Object id, Class entityClass, Class targe
* @param entityClass The class to extract set name from. Must not be {@literal null}.
* @param targetClass The class to map the record to.
* @param query The {@link Query} to filter results. Optional argument (null if no filtering required).
- * @return The matching records mapped to targetClass's type if provided
- * (otherwise to entityClass's type), or an empty list if no documents found.
+ * @return The matching records mapped to targetClass's type if provided (otherwise to entityClass's type), or an
+ * empty list if no documents found.
*/
List> findByIdsUsingQuery(Collection> ids, Class entityClass, Class targetClass,
@Nullable Query query);
@@ -748,8 +724,8 @@ List> findByIdsUsingQuery(Collection> ids, Class entityClass, Clas
* @param targetClass The class to map the record to.
* @param setName Set name to use.
* @param query The {@link Query} to filter results. Optional argument (null if no filtering required).
- * @return The matching records mapped to targetClass's type if provided
- * (otherwise to entityClass's type), or an empty list if no documents found.
+ * @return The matching records mapped to targetClass's type if provided (otherwise to entityClass's type), or an
+ * empty list if no documents found.
*/
List> findByIdsUsingQuery(Collection> ids, Class entityClass, Class targetClass, String setName,
@Nullable Query query);
@@ -758,8 +734,7 @@ List> findByIdsUsingQuery(Collection> ids, Class entityClass, Clas
* Find records in the given entityClass's set using a query and map them to the given class type.
*
* @param query The {@link Query} to filter results. Must not be {@literal null}.
- * @param entityClass The class to extract set name from and to map the records to. Must not be
- * {@literal null}.
+ * @param entityClass The class to extract set name from and to map the records to. Must not be {@literal null}.
* @return A Stream of matching records mapped to entityClass type.
*/
Stream find(Query query, Class entityClass);
@@ -787,8 +762,7 @@ List> findByIdsUsingQuery(Collection> ids, Class entityClass, Clas
/**
* Find all records in the given entityClass's set and map them to the given class type.
*
- * @param entityClass The class to extract set name from and to map the records to. Must not be
- * {@literal null}.
+ * @param entityClass The class to extract set name from and to map the records to. Must not be {@literal null}.
* @return A Stream of matching records mapped to entityClass type.
*/
Stream findAll(Class entityClass);
@@ -854,8 +828,7 @@ List> findByIdsUsingQuery(Collection> ids, Class entityClass, Clas
* @param offset The offset to start the range from.
* @param limit The limit of the range.
* @param sort The sort to affect the order of the returned Stream of documents.
- * @param entityClass The class to extract set name from and to map the records to. Must not be
- * {@literal null}.
+ * @param entityClass The class to extract set name from and to map the records to. Must not be {@literal null}.
* @return A Stream of matching records mapped to entityClass type.
*/
Stream findInRange(long offset, long limit, Sort sort, Class entityClass);
@@ -887,8 +860,8 @@ List> findByIdsUsingQuery(Collection> ids, Class entityClass, Clas
Stream findInRange(long offset, long limit, Sort sort, Class targetClass, String setName);
/**
- * Find records in the given entityClass set using a query and map them to the given target class type. If the
- * query has pagination and/or sorting, post-processing must be applied separately.
+ * Find records in the given entityClass set using a query and map them to the given target class type. If the query
+ * has pagination and/or sorting, post-processing must be applied separately.
*
* @param entityClass The class to extract set name from. Must not be {@literal null}.
* @param targetClass The class to map the records to.
diff --git a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java
index d46a6572f..ce5160480 100644
--- a/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java
+++ b/src/main/java/org/springframework/data/aerospike/core/AerospikeTemplate.java
@@ -44,9 +44,9 @@
import org.springframework.data.aerospike.query.QueryEngine;
import org.springframework.data.aerospike.query.cache.IndexRefresher;
import org.springframework.data.aerospike.repository.query.Query;
+import org.springframework.data.aerospike.server.version.ServerVersionSupport;
import org.springframework.data.aerospike.utility.Utils;
import org.springframework.data.domain.Sort;
-import org.springframework.data.keyvalue.core.IterableConverter;
import org.springframework.data.mapping.PropertyHandler;
import org.springframework.data.util.StreamUtils;
import org.springframework.util.Assert;
@@ -66,6 +66,9 @@
import java.util.stream.IntStream;
import java.util.stream.Stream;
+import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.INSERT_OPERATION;
+import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.SAVE_OPERATION;
+import static org.springframework.data.aerospike.core.BaseAerospikeTemplate.OperationType.UPDATE_OPERATION;
import static org.springframework.data.aerospike.core.CoreUtils.getDistinctPredicate;
import static org.springframework.data.aerospike.core.CoreUtils.operations;
import static org.springframework.data.aerospike.core.CoreUtils.verifyUnsortedWithOffset;
@@ -88,6 +91,7 @@ public class AerospikeTemplate extends BaseAerospikeTemplate implements Aerospik
IndexesCacheRefresher {
private static final Pattern INDEX_EXISTS_REGEX_PATTERN = Pattern.compile("^FAIL:(-?\\d+).*$");
+
private final IAerospikeClient client;
private final QueryEngine queryEngine;
private final IndexRefresher indexRefresher;
@@ -98,8 +102,10 @@ public AerospikeTemplate(IAerospikeClient client,
AerospikeMappingContext mappingContext,
AerospikeExceptionTranslator exceptionTranslator,
QueryEngine queryEngine,
- IndexRefresher indexRefresher) {
- super(namespace, converter, mappingContext, exceptionTranslator, client.getWritePolicyDefault());
+ IndexRefresher indexRefresher,
+ ServerVersionSupport serverVersionSupport) {
+ super(namespace, converter, mappingContext, exceptionTranslator, client.getWritePolicyDefault(),
+ serverVersionSupport);
this.client = client;
this.queryEngine = queryEngine;
this.indexRefresher = indexRefresher;
@@ -150,17 +156,46 @@ public void save(T document, String setName) {
@Override
public void saveAll(Iterable documents) {
- Assert.notNull(documents, "Documents for saving must not be null!");
+ validateForBatchWrite(documents, "Documents for saving");
+
saveAll(documents, getSetName(documents.iterator().next()));
}
@Override
public void saveAll(Iterable documents, String setName) {
- Assert.notNull(documents, "Documents for saving must not be null!");
Assert.notNull(setName, "Set name must not be null!");
+ validateForBatchWrite(documents, "Documents for saving");
+
+ applyBufferedBatchWrite(documents, setName, SAVE_OPERATION);
+ }
+
+ private void applyBufferedBatchWrite(Iterable documents, String setName, OperationType operationType) {
+ int batchSize = converter.getAerospikeDataSettings().getBatchWriteSize();
+ List docsList = new ArrayList<>();
+
+ for (T doc : documents) {
+ if (batchWriteSizeMatch(batchSize, docsList.size())) {
+ batchWriteAllDocuments(docsList, setName, operationType);
+ docsList.clear();
+ }
+ docsList.add(doc);
+ }
+ if (!docsList.isEmpty()) {
+ batchWriteAllDocuments(docsList, setName, operationType);
+ }
+ }
+ private void batchWriteAllDocuments(List documents, String setName, OperationType operationType) {
List> batchWriteDataList = new ArrayList<>();
- documents.forEach(document -> batchWriteDataList.add(getBatchWriteForSave(document, setName)));
+ switch (operationType) {
+ case SAVE_OPERATION ->
+ documents.forEach(document -> batchWriteDataList.add(getBatchWriteForSave(document, setName)));
+ case INSERT_OPERATION ->
+ documents.forEach(document -> batchWriteDataList.add(getBatchWriteForInsert(document, setName)));
+ case UPDATE_OPERATION ->
+ documents.forEach(document -> batchWriteDataList.add(getBatchWriteForUpdate(document, setName)));
+ default -> throw new IllegalArgumentException("Unexpected operation name: " + operationType);
+ }
List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList();
try {
@@ -170,13 +205,13 @@ public void saveAll(Iterable documents, String setName) {
throw translateError(e);
}
- checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, "save");
+ checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, operationType);
}
private void checkForErrorsAndUpdateVersion(List> batchWriteDataList,
- List batchWriteRecords, String commandName) {
+ List batchWriteRecords, OperationType operationType) {
boolean errorsFound = false;
- for (AerospikeTemplate.BatchWriteData data : batchWriteDataList) {
+ for (BaseAerospikeTemplate.BatchWriteData data : batchWriteDataList) {
if (!errorsFound && batchRecordFailed(data.batchRecord())) {
errorsFound = true;
}
@@ -186,7 +221,7 @@ private void checkForErrorsAndUpdateVersion(List> batchWri
}
if (errorsFound) {
- AerospikeException e = new AerospikeException("Errors during batch " + commandName);
+ AerospikeException e = new AerospikeException("Errors during batch " + operationType);
throw new AerospikeException.BatchRecordArray(batchWriteRecords.toArray(BatchRecord[]::new), e);
}
}
@@ -221,27 +256,17 @@ public void insert(T document, String setName) {
@Override
public void insertAll(Iterable extends T> documents) {
- Assert.notNull(documents, "Documents must not be null!");
+ validateForBatchWrite(documents, "Documents for insert");
+
insertAll(documents, getSetName(documents.iterator().next()));
}
@Override
public void insertAll(Iterable extends T> documents, String setName) {
- Assert.notNull(documents, "Documents for inserting must not be null!");
Assert.notNull(setName, "Set name must not be null!");
+ validateForBatchWrite(documents, "Documents for insert");
- List> batchWriteDataList = new ArrayList<>();
- documents.forEach(document -> batchWriteDataList.add(getBatchWriteForInsert(document, setName)));
-
- List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList();
- try {
- // requires server ver. >= 6.0.0
- client.operate(null, batchWriteRecords);
- } catch (AerospikeException e) {
- throw translateError(e);
- }
-
- checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, "insert");
+ applyBufferedBatchWrite(documents, setName, INSERT_OPERATION);
}
@Override
@@ -318,37 +343,27 @@ public void update(T document, String setName, Collection fields) {
@Override
public void updateAll(Iterable documents) {
- Assert.notNull(documents, "Documents must not be null!");
+ validateForBatchWrite(documents, "Documents for update");
+
updateAll(documents, getSetName(documents.iterator().next()));
}
@Override
public void updateAll(Iterable documents, String setName) {
- Assert.notNull(documents, "Documents must not be null!");
Assert.notNull(setName, "Set name must not be null!");
+ validateForBatchWrite(documents, "Documents for update");
- List> batchWriteDataList = new ArrayList<>();
- documents.forEach(document -> batchWriteDataList.add(getBatchWriteForUpdate(document, setName)));
-
- List batchWriteRecords = batchWriteDataList.stream().map(BatchWriteData::batchRecord).toList();
- try {
- // requires server ver. >= 6.0.0
- client.operate(null, batchWriteRecords);
- } catch (AerospikeException e) {
- throw translateError(e);
- }
-
- checkForErrorsAndUpdateVersion(batchWriteDataList, batchWriteRecords, "update");
+ applyBufferedBatchWrite(documents, setName, UPDATE_OPERATION);
}
- @Deprecated
+ @Deprecated(since = "4.6.0", forRemoval = true)
@Override
public void delete(Class entityClass) {
Assert.notNull(entityClass, "Class must not be null!");
delete(getSetName(entityClass));
}
- @Deprecated
+ @Deprecated(since = "4.6.0", forRemoval = true)
@Override
public boolean delete(Object id, Class entityClass) {
Assert.notNull(entityClass, "Class must not be null!");
@@ -405,30 +420,35 @@ public boolean deleteById(Object id, String setName) {
@Override
public void deleteByIds(Iterable> ids, Class entityClass) {
- Assert.notNull(ids, "List of ids must not be null!");
Assert.notNull(entityClass, "Class must not be null!");
+ validateForBatchWrite(ids, "IDs");
+
deleteByIds(ids, getSetName(entityClass));
}
@Override
- public void deleteByIds(Iterable> ids, String setName) {
- Assert.notNull(ids, "List of ids must not be null!");
+ public void
+ deleteByIds(Iterable> ids, String setName) {
Assert.notNull(setName, "Set name must not be null!");
-
- deleteByIds(IterableConverter.toList(ids), setName);
- }
-
- @Override
- public void deleteByIds(Collection> ids, Class entityClass) {
- Assert.notNull(ids, "List of ids must not be null!");
- Assert.notNull(entityClass, "Class must not be null!");
- deleteByIds(ids, getSetName(entityClass));
+ validateForBatchWrite(ids, "IDs");
+
+ int batchSize = converter.getAerospikeDataSettings().getBatchWriteSize();
+ List