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); // <-Das ist nicht gut
}
Dies ist die Repository-Klasse, die ich erstellt habe, um DynamoDB als DB für Spring-Anwendungen zu verwenden. Aber ** dies führt dazu, dass die Anwendung nicht gestartet werden kann. ** ** ** Als Gegenmaßnahme werde ich über das Erstellen einer separaten Repository-Klasse sprechen.
In Spring liegt der Zugriff auf die Datenbank in der Verantwortung des Repositorys. Wenn Sie jedoch eine Klasse erstellen, die die von verschiedenen Bibliotheken bereitgestellte Repository-Schnittstelle erbt, müssen Sie die Methode häufig nicht selbst vorbereiten.
Spring-data-dynamodb ist praktisch, wenn DynamoDB auch als DB ausgewählt ist. Es scheint jedoch, dass ** Datenerfassung durch Abfragespezifikation (Eingrenzung durch andere Felder als Hash-Schlüssel, Bereichsschlüssel usw.) nicht implementiert ist **. Deshalb, Beim Starten der Spring-Anwendung wird ein Fehler ausgegeben und der Start schlägt fehl.
Der Fehler beim Starten der Anwendung war wie folgt.
Unterkante der Stapelspur
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
Wie Sie bei näherer Betrachtung sehen können, wird dieser Fehler im Frühjahr ausgelöst. Wenn Sie den Fehlersatz grob übersetzen, "Ich habe eine Abfragemethode in diesem Repository definiert, aber es ist keine Abfragesuchstrategie definiert. Die Infrastruktur unterstützt die Abfragemethode nicht explizit!" Es wird sein.
Wenn es mit einer von Spring erstellten Bibliothek abgeschlossen ist, wird die Implementierung des Repositorys auch von Spring durchgeführt, und es sollte garantiert werden, dass die Abfragemethode unterstützt wird, aber die diesmal verwendete Spring-Data-Dynamodb unterstützt sie nicht. Es scheint, dass es nicht garantiert ist.
Die erste Lösung, die ich mir vorstellen kann, besteht darin, die Garantieeinstellungen für die Abfragemethode zu ändern, da dies nicht garantiert ist, aber es scheint, dass diese Bibliothek die Abfragemethode nicht wirklich unterstützt. (GitHub-Problem, das diesem Fall entspricht, wurde entfernt, nachdem es in das TODO des Updates auf Version 5.0.3 aufgenommen wurde. Sie können sehen, wie es passiert ist. Es scheint schwierig zu sein ...)
Also, obwohl es gewaltsam ist, habe ich beschlossen, es selbst zu implementieren. (Vielleicht gibt es andere Problemumgehungen.)
Verwenden Sie DynamoDBMapper, um eine Klasse zu definieren, die dem Repository selbst entspricht.
Verwenden Sie den von AWS bereitgestellten DynamoDBMapper, um eine Abfragemethode zu implementieren, die die Abfragesuche von DynamoDB reproduziert. Es wird anhand eines Beispiels in der offiziellen AWS-Dokumentation gezeigt. Bitte beziehen Sie sich darauf. (Bei der Implementierung dieses Links wird das Modell auch in der Klasse implementiert, die dem Repository entspricht, aber ich implementiere das Modell als separate Klasse.) https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBMapper.QueryScanExample.html
Hier ist das Endprodukt. (Wie lautet der Klassenname in einem solchen Fall ...) Daten in DynamoDB werden an anderer Stelle als Tweet-Klasse definiert. Stellen Sie sich vor, die DB hat eine Art Tweet-Information. Diesmal ist es in Ordnung, wenn Sie nur wissen, dass die Tweet-Klasse ein Feld mit dem Namen ** Typ ** hat.
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;
/**
*Feder für den Zugriff auf Dynamo DB-data-In Dynamodb
*Implementierung nicht unterstützter Methoden (z. B. Scan mit Abfrage)
*
* 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"Ist wie ein reserviertes Wort in DynamoDB, also
//Fügen Sie einen Platzhalter in die Abfragezeichenfolge ein und ersetzen Sie ihn später.
.withFilterExpression("#t = :val1")
.withExpressionAttributeNames(
Map.of("#t", "type")
)
.withExpressionAttributeValues(
Map.of(":val1", new AttributeValue().withS(type))
);
return mapper.scan(Tweet.class, scanExpression);
}
}
[Über withExpressionAttributeNames](https://qiita.com/ponponpopon/items/9b723d6210a26d512a9f#%E6%B3%A8-withexpressionattributenames%E3%81%AB%E3%81%A4%E3%81%84%E3%81% A6)
Der Instanz-Mapper der DynamoDBMapper-Klasse wird vom Konstruktor injiziert, dies wird jedoch in der separat vorbereiteten und als Bean registrierten Config-Klasse definiert.
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 {
/**
*AWS DynamoDB-Einstellungen
* @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());
}
}
Derzeit ist es eine Service-Klasse, die die diesmal definierte Methode aufruft. Sie sollten bereits ein Repository injiziert haben, fügen Sie also einfach das Element hinzu, das Sie injizieren möchten.
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; // <-Dieses Mal wurde das Repository hinzugefügt
private final Twitter twitter;
public TweetService(TweetRepository tweetRepository, TweetRepositoryPlugin tweetRepositoryPlugin, Twitter twitter) {
this.tweetRepository = tweetRepository;
this.tweetRepositoryPlugin = tweetRepositoryPlugin; // <-Dieses Mal wurde das Repository hinzugefügt
this.twitter = twitter;
}
//Nachfolgend verschiedene Methodendefinitionen.
Das ist es. Offizielle Erklärung erläutert, wie verschiedene Abfragemuster implementiert werden. Wenn die Erklärung in diesem Artikel nicht ausreicht, beziehen Sie sich bitte darauf.
Wenn jemand andere Lösungen kennt, lassen Sie es mich bitte wissen.
Ich habe Kommentare in den Code aufgenommen, aber ich verwende **. WithExpressionAttributeNames () **, um die Feldnamen zu ersetzen. Der Grund dafür ist, dass das Feld ** Typ ** in der Suchabfrage, die ich dieses Mal angeben wollte, ein reserviertes Wort für DynamoDB zu sein scheint und der folgende Fehler ursprünglich ausgelöst wurde.
Invalid UpdateExpression: Attribute name is a reserved keyword; reserved keyword: type
Sie können diesen Fehler vermeiden, indem Sie den vorherigen Feldnamen ersetzen.
Referenz: https://note.kiriukun.com/entry/20190212-attribute-name-is-a-reserved-keyword-in-dynamodb
Recommended Posts