Saltar al contenido

Spring + webFlux+DyNamoDB

A continuación se mostrara un ejemplo en el cual se condigurara una base de dato MySQL en AWS RDS y se conectara a traves de un servicio REST desarrollado en Spring.

Herramientas usadas:

  • Spring Tool Suit
  • Java 8
  • DynamoDB
  • Apache Maven 3.6.3
  • Spring Boot 2.1.5.RELEASE

1.-Decargar desde el sitio oficial de amazon webservices la base de datos dynamoDB https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html

Selecionar la región deseada y dar clic en la sección de «Download Links«.

Una vez que se descarga el archivo se procede a descomprimir en cualquier lugar de tu computadora y accedemos a la consola para este ejemplo se utlizará la consola de windows.

Para iniciar la base de datos se ejecuta la siguiente instrucción.

java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb -port 8001

Aparecera de la siguiente manera cuando el servicio a levantado correctamente.

Para validar que la base de datos se encuentra arriba ejecutar la siguiente linea , la cual muestra el listado de tablas existentes.

aws dynamodb list-tables --endpoint-url http://localhost:8001

2.-Crear en DynamoDB

Para crear una tabla en DynamoDB ejecutamos el siguiente script en nuestra consola.

aws dynamodb create-table --table-name Music2 --attribute-definitions AttributeName=Artist,AttributeType=S AttributeName=SongTitle,AttributeType=S --key-schema AttributeName=Artist,KeyType=HASH AttributeName=SongTitle,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=10,WriteCapacityUnits=5 --endpoint-url http://localhost:8001

Se valida nuevamente en la consola, ejecutando las instrucciones para validar el listado de tablas que existen en la base de datos.

3.-Crear un proyecto SpringBoot con las siguientes dependencias.

  • SpringBoot 2.1.5.RELEASE
  • SpringWeb
  • Spring Reactive Web

Una vez generado el proyecto importarlo dentro de nuestro editor de código, para este ejemplo se utiliza SpringToolSuit.

Se agregaran dos librerias mas al pom del proyecto para poder ejecutar SpringData con webflux y DynamoDB y son las siguientes.

<dependency>
 <groupId>software.amazon.awssdk</groupId>
 <artifactId>dynamodb</artifactId>
 <version>2.16.60</version>
</dependency>
<dependency>
 <groupId>com.github.derjust</groupId>
 <artifactId>spring-data-dynamodb</artifactId>
 <version>5.1.0</version>
</dependency>

A continuación se configura el archivo application.properties del proyecto en el cual especificaremos los datos para conectarnos a DynamoDB. Podemos no especificar los atributos accessKey y secretKey ya que nos conectaremos a una Base de datos local.

amazon.dynamodb.endpoint=http://localhost:8001/
amazon.aws.accesskey=test1
amazon.aws.secretkey=test1

Unavez que se realizan las configuraciones se crearan las clases que nos permitan agregar y obtener los datos guardados en la base de datos.

Clase DynamoDBConfig contiene el código que permite conectarnos a la base de datos.

package com.example.web.flux.webflux.config;


import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;

import org.socialsignin.spring.data.dynamodb.repository.config.EnableDynamoDBRepositories;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableDynamoDBRepositories
    (basePackages = "com.example.web.flux.webflux.repository")
public class DynamoDBConfig {

    @Value("${amazon.dynamodb.endpoint}")
    private String amazonDynamoDBEndpoint;

    @Value("${amazon.aws.accesskey}")
    private String amazonAWSAccessKey;

    @Value("${amazon.aws.secretkey}")
    private String amazonAWSSecretKey;

    @Bean
    public AmazonDynamoDB amazonDynamoDB(AWSCredentialsProvider awsCredentialsProvider) {
        AmazonDynamoDB amazonDynamoDB
            = AmazonDynamoDBClientBuilder.standard()
            .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(amazonDynamoDBEndpoint, "us-east-1"))
            .withCredentials(awsCredentialsProvider).build();
        return amazonDynamoDB;
    }

    @Bean
    public AWSCredentialsProvider awsCredentialsProvider() {
        return new AWSStaticCredentialsProvider(new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey));
    }

}

La clase Music contiene el codigo del objeto que se mapeara a la tabla music creada en la base de datos DynamoDB.

package com.example.web.flux.webflux.domain;

import java.io.Serializable;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey;

public class MusicId implements Serializable {
	private static final long serialVersionUID = 1L;

	public String artist;
	public String songTitle;
	 @DynamoDBHashKey(attributeName = "Artist")
	public String getArtist() {
		return artist;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}
	
	@DynamoDBRangeKey(attributeName = "SongTitle")
	public String getSongTitle() {
		return songTitle;
	}

	public void setSongTitle(String songTitle) {
		this.songTitle = songTitle;
	}

	@Override
	public String toString() {
		return "Music [artist=" + artist + ", songTitle=" + songTitle + "]";
	}

}
package com.example.web.flux.webflux.domain;

import org.springframework.data.annotation.Id;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBRangeKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;

@DynamoDBTable(tableName = "Music")
public class Music {
	@Id
	private MusicId musicId;
	private String artist;
	private String songTitle;
	 @DynamoDBHashKey(attributeName = "Artist")
	public String getArtist() {
		return artist;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}
	
	@DynamoDBRangeKey(attributeName = "SongTitle")
	public String getSongTitle() {
		return songTitle;
	}

	public void setSongTitle(String songTitle) {
		this.songTitle = songTitle;
	}

	@Override
	public String toString() {
		return "Music [artist=" + artist + ", songTitle=" + songTitle + "]";
	}

}

Se crea la Interfaz MusicRepository la cual nos permitira acceder a los metodos básicos que nos permitiran agregar y consultar datos.

package com.example.web.flux.webflux.repository;

import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.springframework.data.repository.CrudRepository;

import com.example.web.flux.webflux.domain.Music;
import com.example.web.flux.webflux.domain.MusicId;
 

@EnableScan
public interface MusicRepository extends CrudRepository<Music,MusicId> {
	
}

Se debera crear una interfaz y una clase que implemente dicha intecaz que permite agregar los metódos a los cuales se estara accediendo al repositorio de datos.

package com.example.web.flux.webflux.service;

import com.example.web.flux.webflux.domain.Music;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface MusicService {
	Flux<Music> getAll();
	Mono<Music> findMusicByArtist(String id);
	Mono<Music> add(Music music) ;
}
package com.example.web.flux.webflux.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.web.flux.webflux.domain.Music;
import com.example.web.flux.webflux.repository.MusicRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class MusicServiceImpl implements MusicService {
	@Autowired
	private MusicRepository musicRepository;

	@Override
	public Flux<Music> getAll() {
		// TODO Auto-generated method stub
		return Flux.fromIterable(musicRepository.findAll()).switchIfEmpty(Flux.empty());
	}

	@Override
	public Mono<Music> findMusicByArtist(String artist) {
		//Mono<Music> music  = Mono.justOrEmpty(musicRepository.findById(artist));
		return null;
	}


	@Override
	public Mono<Music> add(Music music) {
		Mono<Music> newMusic = Mono.justOrEmpty( musicRepository.save(music));
		return newMusic;
	}

}

Se agrega un controlador el cual permitira acceder a los servicios REST.

package com.example.web.flux.webflux.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.example.web.flux.webflux.domain.Music;
import com.example.web.flux.webflux.service.MusicService;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
public class MusicController {
	@Autowired
	public MusicService musicService;

	@GetMapping("/music")
	public Flux<Music> getAllMusic() {
		return musicService.getAll();
	}

	@GetMapping("/music/{id}")
	public Mono<Music> getMusicById(@PathVariable String id) {
		return musicService.findMusicByArtist(id);
	}

	@PostMapping("/music")
	public Mono<Music> newMusic(@RequestBody Music newMusic) {
		Mono<Music> music = musicService.add(newMusic);
		return music;
	}
}

Realizamos la prueba mediante postman, para guardar datos en la tabla que se creo en DynamoDB.