Usando o Couchbase em um aplicativo Spring

Usando o Couchbase em um aplicativo Spring

1. Introdução

Neste acompanhamento de nossointroduction to Couchbase, criamos um conjunto de serviços Spring que podem ser usados ​​juntos para criar uma camada de persistência básica para um aplicativo Spring sem o uso de Spring Data.

2. Serviço de cluster

Para satisfazer a restrição de que apenas um únicoCouchbaseEnvironment pode estar ativo na JVM, começamos escrevendo um serviço que se conecta a um cluster Couchbase e fornece acesso a depósitos de dados sem expor diretamente oCluster ou instâncias deCouchbaseEnvironment.

2.1. Interface

Aqui está a nossa interfaceClusterService:

public interface ClusterService {
    Bucket openBucket(String name, String password);
}

2.2. Implementação

Nossa classe de implementação instancia aDefaultCouchbaseEnvironmente conecta a um cluster durante a fase@PostConstruct durante a inicialização do contexto Spring.

Isso garante que o cluster não seja nulo e esteja conectado quando a classe for injetada em outras classes de serviço, permitindo que eles abram um ou mais depósitos de dados:

@Service
public class ClusterServiceImpl implements ClusterService {
    private Cluster cluster;

    @PostConstruct
    private void init() {
        CouchbaseEnvironment env = DefaultCouchbaseEnvironment.create();
        cluster = CouchbaseCluster.create(env, "localhost");
    }
...
}

Em seguida, fornecemos umConcurrentHashMap para conter os intervalos abertos e implementamos o métodoopenBucket:

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. Serviço de balde

Dependendo de como você arquitetou seu aplicativo, pode ser necessário fornecer acesso ao mesmo depósito de dados em vários serviços Spring.

Se apenas tentarmos abrir o mesmo depósito em dois ou mais serviços durante a inicialização do aplicativo, o segundo serviço a tentar isso provavelmente encontraráConcurrentTimeoutException.

Para evitar esse cenário, definimos uma interfaceBucketService e uma classe de implementação por bucket. Cada classe de implementação atua como uma ponte entreClusterService e as classes que precisam de acesso direto a um determinadoBucket.

3.1. Interface

Aqui está a nossa interfaceBucketService:

public interface BucketService {
    Bucket getBucket();
}

3.2. Implementação

A seguinte classe fornece acesso ao intervalo “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;
    }
}

Ao injetarClusterService em nossa classe de implementaçãoTutorialBucketService e abrir o balde em um método anotado com@PostConstruct,, garantimos que o balde estará pronto para uso quando oTutorialBucketService for em seguida, injetado em outros serviços.

4. Camada de persistência

Agora que temos um serviço para obter uma instânciaBucket, criaremos uma camada de persistência semelhante a um repositório que fornece operações CRUD para classes de entidade para outros serviços sem expor a instânciaBucket a eles.

4.1. A Pessoa Entidade

Aqui está a classe de entidadePerson que desejamos persistir:

public class Person {

    private String id;
    private String type;
    private String name;
    private String homeTown;

    // standard getters and setters
}

4.2. Convertendo classes de entidade para e de JSON

Para converter classes de entidade de e para os objetosJsonDocument que o Couchbase usa em suas operações de persistência, definimos a interfaceJsonDocumentConverter:

public interface JsonDocumentConverter {
    JsonDocument toDocument(T t);
    T fromDocument(JsonDocument doc);
}

4.3. Implementando o conversor JSON

Em seguida, precisamos implementar umJsonConverter para entidadesPerson.

@Service
public class PersonDocumentConverter
  implements JsonDocumentConverter {
    ...
}

Poderíamos usar a bibliotecaJackson em conjunto com os métodostoJsonefromJson da classeJsonObject para serializar e desserializar as entidades __, no entanto, há sobrecarga adicional ao fazer isso.

Em vez disso, para o métodotoDocument, usaremos os métodos fluentes da classeJsonObject para criar e preencher umJsonObject antes de envolvê-lo emJsonDocument:

@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);
}

E para o métodofromDocument, usaremos o métodogetString da classeJsonObject junto com os setters na classePerson em nosso métodofromDocument:

@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. Interface CRUD

Agora criamos uma interfaceCrudService genérica que define operações de persistência para classes de entidade:

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. Implementando o Serviço CRUD

Com as classes de entidade e conversor em vigor, agora implementamosCrudService para a entidadePerson, injetando o serviço de intervalo e o conversor de documento mostrado acima e recuperando o intervalo durante a inicialização:

@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. Juntando tudo

Agora que temos todas as peças de nossa camada de persistência no lugar, aqui está um exemplo simples de um serviço de registro que usaPersonCrudService para persistir e recuperar registrantes:

@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. Conclusão

Mostramos que, com alguns serviços básicos do Spring, é bastante trivial incorporar o Couchbase em um aplicativo Spring e implementar uma camada de persistência básica sem o uso de dados do Spring.

O código-fonte mostrado neste tutorial está disponível emGitHub project.

Você pode aprender mais sobre o Couchbase Java SDK noCouchbase developer documentation site. oficial