In this section we’ll cover setup of the project from the start.
First, we’ll need to set up the basic project structure. You can do it either manually if you are familiar with the process or do it via Spring Initializr.
In Spring Initializr you’ll need to select:
-
Project: Maven project
-
Language: Java
-
Spring Boot: 3.4.1
-
Java: 17
Press Generate button in the bottom of the page, and you’ll get zip with initial project. Import project into your favorite IDE and run the test. It should be green and that means that you are ready to continue to the next section.
Note
|
Spring Data Aerospike is an Aerospike project, and the dependency is not managed by Spring. It has its own release cycle not included into Spring Boot dependency management. Available versions can be checked on Maven Central. Compatibility table
can be checked for the version of |
In our case we are going to use spring-data-aerospike
5.0.0 as we have Spring Boot
3.4.1.
<dependency>
<groupId>com.aerospike</groupId>
<artifactId>spring-data-aerospike</artifactId>
</dependency>
We will be using Lombok library to omit the boilerplate code in the entity class.
Add lombok
into your pom.xml
:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
We are going to use the movies domain in this project, so to be able to read and save our movies into Aerospike we will need to create:
-
document (entity) class that represents our domain model,
-
repository class that provides CRUD operations.
Movie document class will look the following way:
@Value // (1)
@Document(collection = "demo-service-movies") // (2)
@Builder(toBuilder = true) // (3)
// Spring Data object creation can use all-args constructor instead of reflection which is much faster
@AllArgsConstructor // (4)
public class MovieDocument {
@Id // (5)
String id;
@Field // (6)
String name;
@Field("desc") // (7)
String description;
@Indexed(type = IndexType.NUMERIC, collectionType = IndexCollectionType.DEFAULT) // (8)
@Field
int likes;
@Field
double rating;
}
Document explained:
-
@Value
makes class immutable, all fields are made private and final,toString()
,equals()
,hashCode()
, field getters and all args constructor are generated. -
@Document(collection = "demo-service-movies")
marks a class as an entity to be persisted to Aerospike. It also allows to specify set name, expiration and touch on read values. In current example custom set name is specified viacollection
. Please note that a set name cannot contain the ':' or ';' characters. (See more limitations) -
@Builder
provide Builder API for a class. -
@AllArgsConstructor
creates public all-args constructor for a class (which is hidden by@Builder
). Spring Data can use all-args constructor instead of reflection to gain performance boost for object creation. -
@Id
marks a field as the primary key. -
@Field
is optional, can be set just for the clarity purpose. -
@Field("desc")
configures the name of a field to be used when persisting the document. This value will be used as a bin name instead of the original field name. -
@Indexed
creates secondary index on application startup for the specific field. Note:@Indexed
annotation is not supported for the fields annotated with@Id
,@Expiration
or@Version
annotations.
Note
|
Aerospike has certain limitations including bin name length.
If your document contains field with name that exceeds this limit, specify short name in @Field("shortName") String veeeeeryLoooongFieldName; |
Create Movie Repository interface:
public interface MovieRepository extends AerospikeRepository<MovieDocument, String> { // (1)
}
Repository explained:
-
AerospikeRepository
provides Repository functionality for the entity class (standard CRUD + Aerospike specific operations).
To configure connection to Aerospike you’ll need to create configuration class that extends
AbstractAerospikeDataConfiguration
.
Basic setup requires one of these approaches:
-
Configure
application.properties
usingspring.aerospike*
andspring.data.aerospike*
prefixes. -
Override
getHosts()
andnameSpace()
methods. You might also have to overridegetClientPolicy()
method to specify custom configuration for the Aerospike client, andcustomConverters()
to add custom converters.
For more details, see Configuration documentation.
Simple configuration will look the following way:
@Configuration
@EnableAerospikeRepositories(basePackageClasses = MovieRepository.class)
public class AerospikeConfiguration extends AbstractAerospikeDataConfiguration {
}
To verify that the repository is actually working let’s add some basic tests:
public class MovieRepositoryTests extends SimpleCrudAerospikeDemoApplicationTest {
String id;
MovieDocument movie;
@Autowired
MovieRepository repository;
@BeforeEach
void setUp() {
id = UUID.randomUUID().toString();
movie = MovieDocument.builder()
.id(id)
.name("Back To the Future")
.description("I finally invented something that works!")
.rating(9.3)
.likes(555_555)
.build();
}
@Test
public void saveMovie() {
repository.save(movie);
assertThat(repository.findById(id)).hasValue(movie);
}
@Test
public void exists_returnsTrueIfMovieIsPresent() {
repository.save(movie);
assertThat(repository.existsById(id)).isTrue();
}
@Test
public void deleteExistingMovieById() {
repository.save(movie);
repository.deleteById(id);
assertThat(repository.findById(id)).isNotPresent();
}
@Test
void deleteById_doesNothingForNonExistingMovie() {
repository.deleteById(id);
}
}
For the test purposes you can use
embedded-aerospike
library based on testcontainers
.
Note
|
The spring-cloud-starter-bootstrap dependency is required to be present on classpath.
If you are not using Spring Cloud you can add spring-cloud-starter-bootstrap with test scope.
|
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.playtika.testcontainers</groupId>
<artifactId>embedded-aerospike</artifactId>
<scope>test</scope>
</dependency>
This will set up Aerospike container when the test starts.
Add Aerospike configuration to the test resources:
# Using embedded-aerospike # The version can be controlled via bootstrap.properties spring.aerospike.hosts=${embedded.aerospike.host}:${embedded.aerospike.port} spring.data.aerospike.namespace=${embedded.aerospike.namespace}
and run the tests.
To see demo application go to Simple CRUD Demo.