The latest version of Spring Data Aerospike, 5.0.0, brings a range of exciting new features and improvements. This blog covers some newly introduced features, such as supporting transactions, byte array equality queries, and more querying keywords. An accompanying blog, Spring Data Aerospike 5.0.0: Important updates, covers some of the major updates to the current functionality (updates to application.properties, custom queries API, using filter expression as secondary index fallback, limiting the minimal supported Aerospike Server version).
Supporting transactions
With the release of Aerospike 8, transactions are now supported. This feature allows you to group multiple Aerospike operation requests (commands) into a single atomic and isolated transaction. This guarantees that either all of the commands within a transaction succeed together or all fail, resulting in the rollback of records involved in the transaction so that the records will have the same state as they had prior to the transaction.
Note: Not all Spring Data Aerospike operations can participate in transactions.
Here is a list of the operations that do participate in transactions:
all single record operations
(insert, save, update, add, append, persist, findById, exists, delete
)all batch operations without query (
insertAll, saveAll, findByIds, deleteAll
)queries that include
id
(e.g., repository queries likefindByIdAndName
)
The following operations do not participate in transactions (will not become part of a transaction if included):
truncate
queries that do not include
id
(e.g., repository queries likefindByName
)operations that perform info commands (e.g.,
indexExists
)operations that perform scans (using ScanPolicy)
In Spring Data Aerospike, transactions are typically utilized via @Transactional annotation (declarative approach). This annotation manages transaction boundaries, ensuring methods execute within a transactional context.
Transaction management
Spring offers two models of transaction management: declarative and programmatic. Declarative management is generally more straightforward and reduces boilerplate code, while programmatic management offers more control but at the cost of increased complexity. Declarative transaction management is usually more common in Spring applications due to its simplicity and ease of use. Here is a basic example that uses this approach.
Example
Here are a couple of things needed to start working with transactions using the declarative approach:
- A TransactionManager
must be specified in your Spring configuration.
- Spring configuration must be annotated with @EnableTransactionManagement
.
@Configuration
@EnableTransactionManagement
public class Config {
@Bean
public AerospikeTransactionManager aerospikeTransactionManager(IAerospikeClient client) {
return new AerospikeTransactionManager(client);
}
// Other configuration
}
The following example shows applying @Transactional
annotation to a method.
It ensures that the entire method runs within a transaction context. Spring manages the transaction lifecycle (automatically committing the transaction if the method succeeds or rolling back if it encounters an exception).
@Service
public class MyService {
@Autowired
protected AerospikeTemplate template;
@Transactional
public void performDatabaseOperations() {
// Perform database operations, e.g., multiple batch writes
template.insertAll(List.of(new SampleClasses.DocumentWithPrimitiveIntId(301),
new SampleClasses.DocumentWithPrimitiveIntId(401)));
template.insertAll(List.of(new SampleClasses.DocumentWithPrimitiveIntId(501),
new SampleClasses.DocumentWithPrimitiveIntId(601)));
}
}
Supporting byte array equality queries
Byte array equality queries compare two-byte arrays to determine if they contain the same data. A byte array is essentially a sequence of bytes, often used to represent binary data like files, images, or serialized objects.
In Spring Data Aerospike, you can now perform byte array equality queries. The prerequisite is having Aerospike Server version 7* or higher.
Let us consider an example.
Example
The Person
class represents a person with an ID, first name, and a byte array field. The class uses Spring Data Aerospike’s @Document
(marks a domain object to be persisted to Aerospike Database) and @Id
(identifies a field to store a unique id for a Java object) annotations.
@Data
@Document
public class Person {
@Id
private String id;
private String firstName;
private byte[] byteArray;
}
An instance of Person
named stefan
is created with predefined values.
static final Person stefan = new Person(“id1”, "Stefan", new byte[]{1, 0, 1, 1, 0, 0, 0, 1});
The byte array equality query method is added to the PersonRepository
interface. For it to function properly, the repository needs to be referenced in configuration via the @EnableAerospikeRepositories
annotation.
public interface PersonRepository<P extends Person> extends AerospikeRepository<P, String> {
List<P> findByByteArray(byte[] byteArray);
// Other query methods
}
@Configuration
@EnableAerospikeRepositories(basePackageClasses = PersonRepository.class)
class ApplicationConfig extends AbstractAerospikeDataConfiguration {
// Actual configuration
}
PersonRepository
is autowired, and the query method is used to find entities with the byte array equal to the given parameter.
@Autowired
protected PersonRepository<Person> repository;
List<Person> result = repository.findByByteArray(new byte[]{1, 0, 1, 1, 0, 0, 0, 1});
assertThat(result).containsOnly(stefan);
Supporting cacheable sync option
Spring’s @Cacheable
annotation stores method results in a cache, enhancing efficiency by avoiding redundant operations. The sync
attribute ensures that if multiple threads attempt to load the same data concurrently, only one thread will trigger the caching process while the others wait for the result, preventing redundant cache loading.
Let’s illustrate it with an example of a method called cacheableMethodSynchronized
. Each time the method is invoked, the noOfCalls
variable is incremented to track how many times the method is called. A new instance of CachedObject
is returned and cached.
@Cacheable(value = "TEST", sync = true)
public CachedObject cacheableMethodSynchronized(String param) {
noOfCalls++;
return new CachedObject();
}
In this example, the @Cacheable
annotation stores the results of the cacheableMethodSynchronized
method in a cache named "TEST." The sync = true
attribute ensures that when multiple threads concurrently invoke this method with the same parameter, only one thread will execute the method and increment noOfCalls
value.
Supporting existsBy, countBy, deleteBy keywords
In Spring Data, the existsBy
, countBy
, and deleteBy
keywords
simplify common database operations: existsBy
checks whether at least one entity matching the given condition exists in the database, countBy
queries the total number of entities matching a given condition, and deleteBy
allows for the removal of entities that match a specific query.
Starting with release 5.0.0, Spring Data Aerospike supports using these keywords in query methods. Let's look at some examples.
Examples
We have our test data saved in the Aerospike Database: two Person
objects with the same name, “Leroi.”
Person leroi = new Person(“id3”, "Leroi");
Person leroi2 = new Person(“id4”, "Leroi");
repository.saveAll(List.of(leroi, leroi2));
Let us determine whether at least one Person called “Leroi” exists in the DB.
boolean result = repository.existsByFirstName("Leroi");
assertThat(result).isTrue();
Now, we count the entities (documents) in the DB and see that there are two of them.
long result = repository.countByFirstName("Leroi");
assertThat(result).isEqualTo(2);
We run a delete query and check that the documents are removed from the DB.
assertThat(repository.findByFirstName("Leroi")).isNotEmpty().hasSize(2);
repository.deleteByFirstName("Leroi");
assertThat(repository.findByFirstName("Leroi")).isEmpty();
Learn more about Spring Data Aerospike 5.0.0
Read the second part for details on the major updates in release 5.0.0 compared to the previous version. To explore the Spring Data Aerospike project more deeply, explore our GitHub page, where you can find comprehensive information. Check out the release notes for Spring Data Aerospike 5.0.0 for additional insights.