Skip to content

Latest commit

 

History

History
297 lines (220 loc) · 9.85 KB

getting-started.adoc

File metadata and controls

297 lines (220 loc) · 9.85 KB

Spring Data Aerospike: Getting Started

How to set up Spring Data Aerospike in a Spring Boot application for basic CRUD operations

Setting up the project

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:

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.

Adding spring-data-aerospike dependency

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 spring-data-aerospike that is compatible with the specific Spring Boot and Aerospike Server versions.

In our case we are going to use spring-data-aerospike 5.0.0 as we have Spring Boot 3.4.1.

pom.xml
        <dependency>
            <groupId>com.aerospike</groupId>
            <artifactId>spring-data-aerospike</artifactId>
        </dependency>

Creating Aerospike repository

We will be using Lombok library to omit the boilerplate code in the entity class. Add lombok into your pom.xml:

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:

  1. document (entity) class that represents our domain model,

  2. repository class that provides CRUD operations.

Movie document class will look the following way:

MovieDocument.java
@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:

  1. @Value makes class immutable, all fields are made private and final, toString(), equals(), hashCode(), field getters and all args constructor are generated.

  2. @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 via collection. Please note that a set name cannot contain the ':' or ';' characters. (See more limitations)

  3. @Builder provide Builder API for a class.

  4. @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.

  5. @Id marks a field as the primary key.

  6. @Field is optional, can be set just for the clarity purpose.

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

  8. @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 annotation:

    @Field("shortName")
    String veeeeeryLoooongFieldName;

Create Movie Repository interface:

MovieRepository.java
public interface MovieRepository extends AerospikeRepository<MovieDocument, String> { // (1)
}

Repository explained:

  1. AerospikeRepository provides Repository functionality for the entity class (standard CRUD + Aerospike specific operations).

Configuring connection to Aerospike

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 using spring.aerospike* and spring.data.aerospike* prefixes.

  • Override getHosts() and nameSpace() methods. You might also have to override getClientPolicy() method to specify custom configuration for the Aerospike client, and customConverters() to add custom converters.

For more details, see Configuration documentation.

Simple configuration will look the following way:

AerospikeConfiguration.java
@Configuration
@EnableAerospikeRepositories(basePackageClasses = MovieRepository.class)
public class AerospikeConfiguration extends AbstractAerospikeDataConfiguration {

}

Testing

To verify that the repository is actually working let’s add some basic tests:

MovieRepositoryTests.java
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.
pom.xml
        <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:

/test/resources/application.properties
# 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.