Использование Couchbase в приложении Spring

Использование Couchbase в приложении Spring

1. Вступление

В этом продолжении нашегоintroduction to Couchbase мы создаем набор служб Spring, которые можно использовать вместе для создания базового уровня сохраняемости для приложения Spring без использования данных Spring.

2. Кластерная служба

Чтобы удовлетворить ограничение, заключающееся в том, что в JVM может быть активен только одинCouchbaseEnvironment, мы начинаем с написания службы, которая подключается к кластеру Couchbase и предоставляет доступ к сегментам данных, не открывая напрямуюCluster илиCouchbaseEnvironment экземпляров.

2.1. Интерфейс

Вот наш интерфейсClusterService:

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

2.2. Реализация

Наш класс реализации создает экземплярDefaultCouchbaseEnvironment и подключается к кластеру на этапе@PostConstruct во время инициализации контекста Spring.

Это гарантирует, что кластер не является нулевым и что он подключен, когда класс внедряется в другие классы обслуживания, что позволяет им открывать одно или несколько сегментов данных:

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

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

Затем мы предоставляемConcurrentHashMap для хранения открытых сегментов и реализуем методopenBucket:

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. Ковш Сервис

В зависимости от того, как вы разрабатываете свое приложение, вам может потребоваться предоставить доступ к одному и тому же контейнеру данных в нескольких сервисах Spring.

Если мы просто попытались открыть одну и ту же корзину в двух или более службах во время запуска приложения, вторая служба, которая попытается это сделать, скорее всего, столкнется сConcurrentTimeoutException.

Чтобы избежать этого сценария, мы определяем интерфейсBucketService и класс реализации для каждой корзины. Каждый класс реализации действует как мост междуClusterService и классами, которым требуется прямой доступ к конкретномуBucket.

3.1. Интерфейс

Вот наш интерфейсBucketService:

public interface BucketService {
    Bucket getBucket();
}

3.2. Реализация

Следующий класс предоставляет доступ к корзине «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;
    }
}

ВнедривClusterService в наш класс реализацииTutorialBucketService и открыв ведро в методе, аннотированном@PostConstruct,, мы убедились, что корзина будет готова к использованию, когдаTutorialBucketService будет затем вводится в другие службы.

4. Постоянный слой

Теперь, когда у нас есть служба для получения экземпляраBucket, мы создадим уровень персистентности, подобный репозиторию, который предоставляет операции CRUD для классов сущностей другим службам, не открывая им экземплярBucket.

4.1. Личность Сущность

Вот класс сущностиPerson, который мы хотим сохранить:

public class Person {

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

    // standard getters and setters
}

4.2. Преобразование классов сущностей в JSON и из него

Чтобы преобразовать классы сущностей в объектыJsonDocument, которые Couchbase использует в своих операциях сохранения, и обратно, мы определяем интерфейсJsonDocumentConverter:

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

4.3. Реализация конвертера JSON

Затем нам нужно реализоватьJsonConverter для объектовPerson.

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

Мы могли бы использовать библиотекуJackson вместе с методамиtoJson иfromJson классаJsonObject для сериализации и десериализации сущностей __, однако при этом возникают дополнительные накладные расходы.

Вместо этого для методаtoDocument мы будем использовать плавные методы классаJsonObject для создания и заполненияJsonObject перед тем, как заключить его вJsonDocument:

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

А для методаfromDocument мы будем использовать методgetString классаJsonObject вместе с установщиками в классеPerson в нашем методеfromDocument:

@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 Интерфейс

Теперь мы создаем общий интерфейсCrudService, который определяет операции сохранения для классов сущностей:

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. Внедрение службы CRUD

Имея классы сущности и преобразователя, мы теперь реализуемCrudService для сущностиPerson, внедряя службу корзины и преобразователь документов, показанные выше, и извлекаем сегмент во время инициализации:

@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. Собираем все вместе

Теперь, когда у нас есть все части нашего уровня сохраняемости, вот простой пример службы регистрации, которая используетPersonCrudService для сохранения и извлечения владельцев регистрации:

@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. Заключение

Мы показали, что с несколькими базовыми сервисами Spring довольно просто включить Couchbase в приложение Spring и реализовать базовый уровень персистентности без использования Spring Data.

Исходный код, показанный в этом руководстве, доступен вGitHub project.

Вы можете узнать больше о Couchbase Java SDK на официальном сайтеCouchbase developer documentation site.