Verwenden von Couchbase in einer Spring-Anwendung
1. Einführung
In diesem Follow-up zu unserenintroduction to Couchbase erstellen wir eine Reihe von Spring-Diensten, die zusammen verwendet werden können, um eine grundlegende Persistenzschicht für eine Spring-Anwendung ohne Verwendung von Spring-Daten zu erstellen.
2. Clusterdienst
Um die Einschränkung zu erfüllen, dass nur ein einzigesCouchbaseEnvironment in der JVM aktiv sein darf, schreiben wir zunächst einen Dienst, der eine Verbindung zu einem Couchbase-Cluster herstellt und Zugriff auf Daten-Buckets bietet, ohne dieCluster direkt offenzulegen oderCouchbaseEnvironment Instanzen.
2.1. Schnittstelle
Hier ist unsereClusterService-Schnittstelle:
public interface ClusterService {
Bucket openBucket(String name, String password);
}
2.2. Implementierung
Unsere Implementierungsklasse instanziiert einDefaultCouchbaseEnvironment und stellt während der@PostConstruct-Phase während der Spring-Kontextinitialisierung eine Verbindung zu einem Cluster her.
Auf diese Weise wird sichergestellt, dass der Cluster nicht null ist und dass eine Verbindung hergestellt wird, wenn die Klasse in andere Serviceklassen eingefügt wird. Auf diese Weise können sie einen oder mehrere Datenbereiche öffnen:
@Service
public class ClusterServiceImpl implements ClusterService {
private Cluster cluster;
@PostConstruct
private void init() {
CouchbaseEnvironment env = DefaultCouchbaseEnvironment.create();
cluster = CouchbaseCluster.create(env, "localhost");
}
...
}
Als nächstes stellen wirConcurrentHashMap bereit, um die offenen Buckets zu enthalten, und implementieren die MethodeopenBucket:
private Map buckets = new ConcurrentHashMap<>();
@Override
synchronized public Bucket openBucket(String name, String password) {
if(!buckets.containsKey(name)) {
Bucket bucket = cluster.openBucket(name, password);
buckets.put(name, bucket);
}
return buckets.get(name);
}
3. Eimerservice
Je nachdem, wie Sie Ihre Anwendung architektonisch gestalten, müssen Sie möglicherweise in mehreren Spring-Services Zugriff auf denselben Daten-Bucket gewähren.
Wenn wir lediglich versucht haben, denselben Bucket in zwei oder mehr Diensten während des Anwendungsstarts zu öffnen, tritt beim zweiten Dienst, der dies versucht, wahrscheinlich einConcurrentTimeoutException auf.
Um dieses Szenario zu vermeiden, definieren wir eineBucketService-Schnittstelle und eine Implementierungsklasse pro Bucket. Jede Implementierungsklasse fungiert als Brücke zwischen denClusterService und den Klassen, die direkten Zugriff auf bestimmteBucket benötigen.
3.1. Schnittstelle
Hier ist unsereBucketService-Schnittstelle:
public interface BucketService {
Bucket getBucket();
}
3.2. Implementierung
Die folgende Klasse bietet Zugriff auf den Bucket "example-tutorial":
@Service
@Qualifier("TutorialBucketService")
public class TutorialBucketService implements BucketService {
@Autowired
private ClusterService couchbase;
private Bucket bucket;
@PostConstruct
private void init() {
bucket = couchbase.openBucket("example-tutorial", "");
}
@Override
public Bucket getBucket() {
return bucket;
}
}
Durch Einfügen derClusterService in unsere ImplementierungsklasseTutorialBucketService und Öffnen des Buckets in einer mit@PostConstruct, versehenen Methode haben wir sichergestellt, dass der Bucket zur Verwendung bereit ist, wennTutorialBucketService ist dann in andere Dienste injiziert.
4. Persistenzschicht
Nachdem wir einen Dienst eingerichtet haben, um eineBucket-Instanz abzurufen, erstellen wir eine repositoryähnliche Persistenzschicht, die CRUD-Operationen für Entitätsklassen für andere Dienste bereitstellt, ohne dieBucket-Instanz für sie verfügbar zu machen.
4.1. Die Personeneinheit
Hier ist die EntitätsklassePerson, die wir beibehalten möchten:
public class Person {
private String id;
private String type;
private String name;
private String homeTown;
// standard getters and setters
}
4.2. Konvertieren von Entitätsklassen in und von JSON
Um Entitätsklassen in und von denJsonDocument-Objekten zu konvertieren, die Couchbase für seine Persistenzoperationen verwendet, definieren wir dieJsonDocumentConverter-Schnittstelle:
public interface JsonDocumentConverter {
JsonDocument toDocument(T t);
T fromDocument(JsonDocument doc);
}
4.3. Implementierung des JSON-Konverters
Als nächstes müssen wirJsonConverter fürPerson Entitäten implementieren.
@Service
public class PersonDocumentConverter
implements JsonDocumentConverter {
...
}
Wir könnten die BibliothekJacksonin Verbindung mit den MethodentoJson undfromJson der KlassenJsonObject verwenden, um die __ Entitäten zu serialisieren und zu deserialisieren. Dies ist jedoch mit zusätzlichem Aufwand verbunden.
Stattdessen verwenden wir für dietoDocument-Methode die fließenden Methoden derJsonObject-Klasse, um einJsonObject zu erstellen und zu füllen, bevor es mit einemJsonDocument umbrochen wird:
@Override
public JsonDocument toDocument(Person p) {
JsonObject content = JsonObject.empty()
.put("type", "Person")
.put("name", p.getName())
.put("homeTown", p.getHomeTown());
return JsonDocument.create(p.getId(), content);
}
Und für die MethodefromDocumentverwenden wir die MethodegetStringder KlasseJsonObjectzusammen mit den Setzern in der KlassePersonin unserer MethodefromDocument:
@Override
public Person fromDocument(JsonDocument doc) {
JsonObject content = doc.content();
Person p = new Person();
p.setId(doc.id());
p.setType("Person");
p.setName(content.getString("name"));
p.setHomeTown(content.getString("homeTown"));
return p;
}
4.4. CRUD-Schnittstelle
Wir erstellen jetzt eine generischeCrudService-Schnittstelle, die Persistenzoperationen für Entitätsklassen definiert:
public interface CrudService {
void create(T t);
T read(String id);
T readFromReplica(String id);
void update(T t);
void delete(String id);
boolean exists(String id);
}
4.5. Implementierung des CRUD-Dienstes
Mit den vorhandenen Entitäts- und Konverterklassen implementieren wir jetztCrudService für die EntitätPerson, injizieren den oben gezeigten Bucket-Service und Dokumentkonverter und rufen den Bucket während der Initialisierung ab:
@Service
public class PersonCrudService implements CrudService {
@Autowired
private TutorialBucketService bucketService;
@Autowired
private PersonDocumentConverter converter;
private Bucket bucket;
@PostConstruct
private void init() {
bucket = bucketService.getBucket();
}
@Override
public void create(Person person) {
if(person.getId() == null) {
person.setId(UUID.randomUUID().toString());
}
JsonDocument document = converter.toDocument(person);
bucket.insert(document);
}
@Override
public Person read(String id) {
JsonDocument doc = bucket.get(id);
return (doc != null ? converter.fromDocument(doc) : null);
}
@Override
public Person readFromReplica(String id) {
List docs = bucket.getFromReplica(id, ReplicaMode.FIRST);
return (docs.isEmpty() ? null : converter.fromDocument(docs.get(0)));
}
@Override
public void update(Person person) {
JsonDocument document = converter.toDocument(person);
bucket.upsert(document);
}
@Override
public void delete(String id) {
bucket.remove(id);
}
@Override
public boolean exists(String id) {
return bucket.exists(id);
}
}
5. Alles zusammenfügen
Nachdem wir nun alle Teile unserer Persistenzschicht eingerichtet haben, finden Sie hier ein einfaches Beispiel für einen Registrierungsdienst, derPersonCrudServiceverwendet, um Registranten zu persistieren und abzurufen:
@Service
public class RegistrationService {
@Autowired
private PersonCrudService crud;
public void registerNewPerson(String name, String homeTown) {
Person person = new Person();
person.setName(name);
person.setHomeTown(homeTown);
crud.create(person);
}
public Person findRegistrant(String id) {
try{
return crud.read(id);
}
catch(CouchbaseException e) {
return crud.readFromReplica(id);
}
}
}
6. Fazit
Wir haben gezeigt, dass es mit einigen grundlegenden Spring-Diensten relativ einfach ist, Couchbase in eine Spring-Anwendung zu integrieren und eine grundlegende Persistenzschicht zu implementieren, ohne Spring-Daten zu verwenden.
Der in diesem Tutorial gezeigte Quellcode ist inGitHub project verfügbar.
Weitere Informationen zum Couchbase Java SDK finden Sie unterCouchbase developer documentation site.