Aerospike 8 introduces a new era of real-time transaction processingLearn more
Blog

Spring Data Aerospike 5.0.0: New features

February 6, 2025 | 5 min read
Untitled
Andrey Grosland
Software Engineer

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 like findByIdAndName)

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 like findByName)

  • 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.

Aerospike Server Enterprise and Standard Edition

Aerospike Server Enterprise and Standard Edition are available as a package for various Linux distributions. Each package contains an installer for Aerospike Server and Aerospike Tools. After downloading a package, refer to the Installation Guide for package installation details.