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