[JAVA] Utiliser la méthode de requête DynamoDB avec Spring Boot

Statut

TweetRepository.java


package com.pontsuyo.anyiine.domain.repository;

import com.pontsuyo.anyiine.domain.model.Tweet;
import java.util.List;
import org.socialsignin.spring.data.dynamodb.repository.EnableScan;
import org.springframework.data.repository.CrudRepository;

@EnableScan
public interface TweetRepository extends CrudRepository<Tweet, Long> {
  @Override
  List<Tweet> findAll();

  List<Tweet> findAllByType(String type);  // <-Ce n'est pas bien
}

Il s'agit de la classe Repository que j'ai créée pour utiliser DynamoDB comme base de données pour les applications Spring. Mais ** cela entraîne l'échec du lancement de l'application. ** ** Donc, en guise de contre-mesure, je parlerai de la création d'une classe de référentiel distincte.

Informations environnementales

Avant

Dans Spring, l'accès à la base de données est de la responsabilité du référentiel, mais si vous créez une classe qui hérite de l'interface du référentiel fournie par diverses bibliothèques, vous n'avez souvent pas besoin de préparer la méthode vous-même.

Si vous sélectionnez également DynamoDB comme base de données, spring-data-dynamodb est pratique. Cependant, il semble que ** l'acquisition de données par spécification de requête (réduction par des champs autres que la clé de hachage, la clé de plage, etc.) n'est pas implémentée **. Pour cette raison, Lors du démarrage de l'application Spring, une erreur est générée et le démarrage échoue.

Erreur réelle

L'erreur lors du démarrage de l'application était la suivante.

Bord inférieur de la trace de pile


Caused by: java.lang.IllegalStateException: You have defined query method in the repository but you don't have any query lookup strategy defined. The infrastructure apparently does not support query methods!
	at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:553) ~[spring-data-commons-2.2.7.RELEASE.jar:2.2.7.RELEASE]
	at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:332) ~[spring-data-commons-2.2.7.RELEASE.jar:2.2.7.RELEASE]
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$5(RepositoryFactoryBeanSupport.java:297) ~[spring-data-commons-2.2.7.RELEASE.jar:2.2.7.RELEASE]
	at org.springframework.data.util.Lazy.getNullable(Lazy.java:212) ~[spring-data-commons-2.2.7.RELEASE.jar:2.2.7.RELEASE]
	at org.springframework.data.util.Lazy.get(Lazy.java:94) ~[spring-data-commons-2.2.7.RELEASE.jar:2.2.7.RELEASE]
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:300) ~[spring-data-commons-2.2.7.RELEASE.jar:2.2.7.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
	... 44 common frames omitted

Comme vous pouvez le voir de plus près, cette erreur est lancée par le printemps. Si vous traduisez grossièrement la phrase d'erreur, "J'ai une méthode de requête définie dans ce référentiel, mais aucune stratégie de recherche de requête n'est définie. L'infrastructure ne prend pas explicitement en charge la méthode de requête!" Ce sera.

S'il est complété avec une bibliothèque créée par Spring, l'implémentation du référentiel est également réalisée par Spring, et il faut garantir que la méthode de requête est prise en charge, mais le spring-data-dynamodb utilisé cette fois ne le prend pas en charge. Il semble que ce ne soit pas garanti.

La première solution qui me vient à l'esprit est de jouer avec les paramètres de garantie de la méthode de requête, car cela n'est pas garanti, mais il semble que cette bibliothèque ne prend pas vraiment en charge la méthode de requête. (Problème GitHub correspondant à ce problème, il a été supprimé après avoir été inclus dans le TODO de la mise à jour v5.0.3. Vous pouvez voir comment cela s'est passé. Cela semble difficile ...)

Donc, même si c'est forcé, j'ai décidé de le mettre en œuvre moi-même. (Il existe peut-être d'autres solutions de contournement.)

Contre-mesures

Utilisez DynamoDBMapper pour définir vous-même une classe équivalente au référentiel.

À l'aide de DynamoDBMapper fourni par AWS, implémentez une méthode de requête qui reproduit la recherche de requête de DynamoDB. Il est montré avec un exemple dans la documentation officielle d'AWS, veuillez donc vous y référer. (Dans l'implémentation de ce lien, model est également implémenté dans la classe équivalente à repository, mais j'implémente model en tant que classe séparée.) https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBMapper.QueryScanExample.html

Voici le produit final. (Quel est le nom de la classe dans un tel cas ...) Les données dans DynamoDB sont définies ailleurs comme une classe Tweet. Imaginez que la base de données dispose d'une sorte d'informations sur les tweets. Cette fois, c'est correct si vous savez simplement que la classe Tweet a un champ nommé ** type **.

TweetRepositoryPlugin.java


package com.pontsuyo.anyiine.domain.repository;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.pontsuyo.anyiine.domain.model.Tweet;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Repository;

/**
 *Ressort utilisé pour accéder à Dynamo DB-data-Dans dynamodb
 *Implémentation de méthodes non prises en charge (telles que l'analyse avec requête)
 *
 * issue: https://github.com/derjust/spring-data-dynamodb/issues/114
 */
@Repository
public class TweetRepositoryPlugin {

  private final DynamoDBMapper mapper;

  public TweetRepositoryPlugin(DynamoDBMapper mapper) {
    this.mapper = mapper;
  }

  public List<Tweet> findAllByType(String type) {
    DynamoDBScanExpression scanExpression =
        new DynamoDBScanExpression()
            // "type"Est comme un mot réservé dans DynamoDB, donc
            //Placez un espace réservé dans la chaîne de requête et remplacez-le ultérieurement.
            .withFilterExpression("#t = :val1")
            .withExpressionAttributeNames(     
                Map.of("#t", "type")
            )
            .withExpressionAttributeValues(
                Map.of(":val1", new AttributeValue().withS(type))
            );

    return mapper.scan(Tweet.class, scanExpression);
  }
}

À propos de withExpressionAttributeNames (https://qiita.com/ponponpopon/items/9b723d6210a26d512a9f#%E6%B3%A8-withexpressionattributenames%E3%81%AB%E3%81%A4%E3%81%84%E3%81% A6)

Le mappeur d'instances de la classe DynamoDBMapper est injecté avec un constructeur, mais celui-ci est défini dans la classe Config préparée séparément et enregistrée en tant que bean.

DynamoDBConfig.java


package com.pontsuyo.anyiine.config;

import com.amazonaws.auth.DefaultAWSCredentialsProviderChain;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import org.socialsignin.spring.data.dynamodb.repository.config.EnableDynamoDBRepositories;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@EnableDynamoDBRepositories(basePackages = "com.pontsuyo.anyiine.domain.repository")
public class DynamoDBConfig {

  /**
   *Paramètres AWS DynamoDB
   * @see com.amazonaws.auth.DefaultAWSCredentialsProviderChain
   * @return
   */
  @Bean
  public AmazonDynamoDB amazonDynamoDB() {
    return AmazonDynamoDBClientBuilder.standard()
        .withCredentials(DefaultAWSCredentialsProviderChain.getInstance())
        .withRegion(Regions.AP_NORTHEAST_1)
        .build();
  }

  @Bean
  public DynamoDBMapper dynamoDBMapper(){
    return new DynamoDBMapper(amazonDynamoDB());
  }
}

Pour le moment, c'est une classe Service qui appelle la méthode définie cette fois. Vous devriez avoir déjà injecté un référentiel, alors ajoutez simplement l'élément que vous souhaitez injecter.

TweetService.java


package com.pontsuyo.anyiine.domain.service;

import com.pontsuyo.anyiine.controller.model.DestroyRequestParameter;
import com.pontsuyo.anyiine.controller.model.UpdateRequestParameter;
import com.pontsuyo.anyiine.domain.model.Tweet;
import com.pontsuyo.anyiine.domain.repository.TweetRepository;
import com.pontsuyo.anyiine.domain.repository.TweetRepositoryPlugin;
import java.util.List;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;

@Slf4j
@Service
public class TweetService {

  private final TweetRepository tweetRepository;
  private final TweetRepositoryPlugin tweetRepositoryPlugin; // <-Cette fois, un référentiel ajouté

  private final Twitter twitter;

  public TweetService(TweetRepository tweetRepository, TweetRepositoryPlugin tweetRepositoryPlugin, Twitter twitter) {
    this.tweetRepository = tweetRepository;
    this.tweetRepositoryPlugin = tweetRepositoryPlugin;  // <-Cette fois, un référentiel ajouté
    this.twitter = twitter;
  }

  //Ci-dessous, diverses définitions de méthodes.

en conclusion

C'est tout. Explication officielle explique comment implémenter divers modèles de requêtes. Si l'explication de cet article ne suffit pas, veuillez vous y référer.

Si quelqu'un connaît d'autres solutions, faites-le moi savoir.

Remarque: À propos de .withExpressionAttributeNames ()

J'ai inclus des commentaires dans le code, mais j'utilise **. WithExpressionAttributeNames () ** pour remplacer les noms de champ. La raison pour laquelle cela est fait est que le champ ** type ** dans la requête de recherche que je voulais spécifier cette fois semble avoir été un mot réservé pour DynamoDB, et l'erreur suivante a été initialement générée.

Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: type

Vous pouvez éviter cette erreur en remplaçant le nom de champ précédent.

référence: https://note.kiriukun.com/entry/20190212-attribute-name-is-a-reserved-keyword-in-dynamodb

Recommended Posts

Utiliser la méthode de requête DynamoDB avec Spring Boot
Utiliser le filtre de servlet avec Spring Boot [compatible Spring Boot 1.x, 2.x]
Définir le paramètre contextuel dans Spring Boot
Multi-projets Spring Boot 2 avec Gradle
Comment utiliser CommandLineRunner dans Spring Batch of Spring Boot
Spring Boot + Springfox springfox-boot-starter 3.0.0 Utilisation
Utiliser Spring JDBC avec Spring Boot
Changements majeurs dans Spring Boot 1.5
NoHttpResponseException dans Spring Boot + WireMock
Mémo de méthode de contrôleur de démarrage à ressort
Comment appeler et utiliser l'API en Java (Spring Boot)
Utilisez thymeleaf3 avec le parent sans spécifier spring-boot-starter-parent dans Spring Boot
Utilisez @ControllerAdvice, @ExceptionHandler, HandlerExceptionResolver dans Spring Boot pour intercepter les exceptions
Comment utiliser Lombok au printemps
Utiliser l'authentification de base avec Spring Boot
Spring Boot Hello World dans Eclipse
Développement d'applications Spring Boot dans Eclipse
Méthode de résolution d'erreur Spring Boot + PostgreSQL
Implémenter l'API REST avec Spring Boot
Qu'est-ce que @Autowired dans Spring Boot?
Utiliser DBUnit pour le test Spring Boot
Implémenter l'application Spring Boot dans Gradle
Comment utiliser ModelMapper (Spring boot)
À partir de Spring Boot 0. Utilisez Spring CLI
Comment utiliser Thymeleaf avec Spring Boot
Lancer un (ancien) projet Spring Boot avec IntelliJ
Créer une image Spring Boot + Docker avec Gradle
Priorité d'accès aux fichiers statiques dans Spring Boot
Sortie du journal Spring Boot au format json
Mémorandum de téléchargement de fichier local avec Spring Boot
Desserrer la vérification de la syntaxe de Thymeleaf dans Spring Boot
[Entraine toi! ] Affichez Hello World avec Spring Boot
Spring Boot + Thymeleaf Boot Strap Méthode d'installation mémo
DI SessionScope Bean dans le filtre Spring Boot 2
Utiliser le cache avec EhCashe 2.x avec Spring Boot
Modifier le délai d'expiration de la session dans Spring Boot
Cookie SameSite dans Spring Boot (Spring Web MVC + Tomcat)
Testez le contrôleur avec Mock MVC dans Spring Boot
Traitement asynchrone avec exécution régulière dans Spring Boot
Utilisez le mode de modèle de texte Thymeleaf de Spring Boot
Comment utiliser MyBatis2 (iBatis) avec Spring Boot 1.4 (Spring 4)
Exécuter un projet Spring Boot avec VS Code
Comment utiliser h2db intégré avec Spring Boot
Sortie des journaux de demande et de réponse avec Spring Boot
Défi Spring Boot
Comment ajouter un chemin de classe dans Spring Boot
Conseils Java - Créez un projet Spring Boot avec Gradle
Forme de botte de printemps
Comment se lier avec un fichier de propriétés dans Spring Boot
[JAVA] [Spring] [MyBatis] Utiliser IN () avec SQL Builder
Spring Boot Rappelez-vous
gae + botte à ressort
Annotations utilisées dans les outils de gestion des tâches Spring Boot
Afficher la tâche Gradle dans le projet Spring Boot
Vérifiez le comportement de getOne, findById et des méthodes de requête avec Spring Boot + Spring Data JPA
Inclure le pot externe dans le package avec Spring boot2 + Maven3
Comment créer un projet Spring Boot dans IntelliJ
Encoder les paramètres de requête avec Uri Components Builder au printemps
SSO avec GitHub OAuth dans l'environnement Spring Boot 1.5.x
Testez la classe injectée sur le terrain dans le test de démarrage Spring sans utiliser le conteneur Spring
Jusqu'à ce que vous commenciez à développer avec Spring Boot dans eclipse 1