Menggunakan Couchbase dalam Aplikasi Musim Semi

1. Perkenalan

Dalam tindak lanjut pengantar kami ke Couchbase, kami membuat satu set layanan Spring yang dapat digunakan bersama untuk membuat lapisan persistensi dasar untuk aplikasi Spring tanpa menggunakan Spring Data.

2. Layanan Cluster

Untuk memenuhi batasan bahwa hanya satu CouchbaseEnvironment yang dapat aktif di JVM, kami mulai dengan menulis layanan yang terhubung ke cluster Couchbase dan menyediakan akses ke bucket data tanpa secara langsung mengekspos instans Cluster atau CouchbaseEnvironment .

2.1. Antarmuka

Berikut adalah antarmuka ClusterService kami :

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

2.2. Penerapan

Kelas implementasi kami membuat instance DefaultCouchbaseEnvironment dan menghubungkan ke cluster selama fase @PostConstruct selama inisialisasi konteks Spring.

Hal ini memastikan bahwa cluster tidak null dan terhubung ketika kelas dimasukkan ke dalam kelas layanan lain, sehingga memungkinkan mereka untuk membuka satu atau beberapa keranjang data:

@Service public class ClusterServiceImpl implements ClusterService { private Cluster cluster; @PostConstruct private void init() { CouchbaseEnvironment env = DefaultCouchbaseEnvironment.create(); cluster = CouchbaseCluster.create(env, "localhost"); } ... }

Selanjutnya, kami menyediakan ConcurrentHashMap untuk menampung bucket terbuka dan mengimplementasikan metode 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. Layanan Bucket

Bergantung pada cara Anda merancang aplikasi, Anda mungkin perlu memberikan akses ke keranjang data yang sama di beberapa layanan Spring.

Jika kita hanya mencoba membuka keranjang yang sama di dua atau lebih layanan selama permulaan aplikasi, layanan kedua yang mencoba ini kemungkinan akan mengalami ConcurrentTimeoutException .

Untuk menghindari skenario ini, kami menetapkan antarmuka BucketService dan kelas implementasi per bucket. Setiap kelas implementasi bertindak sebagai jembatan antara ClusterService dan kelas yang membutuhkan akses langsung ke Bucket tertentu .

3.1. Antarmuka

Berikut adalah antarmuka BucketService kami :

public interface BucketService { Bucket getBucket(); }

3.2. Penerapan

Kelas berikut menyediakan akses ke keranjang " baeldung-tutorial ":

@Service @Qualifier("TutorialBucketService") public class TutorialBucketService implements BucketService { @Autowired private ClusterService couchbase; private Bucket bucket; @PostConstruct private void init() { bucket = couchbase.openBucket("baeldung-tutorial", ""); } @Override public Bucket getBucket() { return bucket; } }

Dengan memasukkan ClusterService di kelas implementasi TutorialBucketService kami dan membuka bucket dalam metode yang dianotasi dengan @PostConstruct, kami telah memastikan bahwa bucket akan siap digunakan saat TutorialBucketService kemudian dimasukkan ke layanan lain.

4. Lapisan Persistensi

Sekarang setelah kita memiliki layanan untuk mendapatkan instance Bucket , kita akan membuat lapisan persistensi seperti repositori yang menyediakan operasi CRUD untuk kelas entitas ke layanan lain tanpa memaparkan instance Bucket kepada mereka.

4.1. Entitas Orang

Berikut adalah kelas entitas Person yang ingin kami pertahankan:

public class Person { private String id; private String type; private String name; private String homeTown; // standard getters and setters }

4.2. Mengonversi Kelas Entitas Ke dan Dari JSON

Untuk mengkonversi kelas entitas ke dan dari JsonDocument objek yang menggunakan Couchbase dalam operasi ketekunan, kita mendefinisikan JsonDocumentConverter antarmuka:

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

4.3. Menerapkan Konverter JSON

Selanjutnya, kita perlu mengimplementasikan JsonConverter untuk entitas Person .

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

Kita bisa menggunakan Jackson perpustakaan dalam hubungannya dengan JSONObject kelas ini toJSON dan fromJson metode untuk cerita bersambung dan deserializeentitas, namun ada biaya tambahan dalam melakukannya.

Sebagai gantinya, untuk metode toDocument , kita akan menggunakan metode fluent dari kelas JsonObject untuk membuat dan mengisi JsonObject sebelum membungkusnya dengan 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); }

Dan untuk metode fromDocument , kita akan menggunakan metode getString kelas JsonObject bersama dengan setter di kelas Person dalam metode fromDocument kita :

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

Kami sekarang membuat antarmuka CrudService generik yang mendefinisikan operasi persistensi untuk kelas entitas:

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. Menerapkan Layanan CRUD

Dengan menerapkan kelas entitas dan konverter, sekarang kami mengimplementasikan CrudService untuk entitas Person , memasukkan layanan bucket dan konverter dokumen yang ditunjukkan di atas dan mengambil bucket selama inisialisasi:

@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. Menyatukan Semuanya

Sekarang setelah kita memiliki semua bagian dari lapisan persistensi kita, berikut adalah contoh sederhana dari layanan pendaftaran yang menggunakan PersonCrudService untuk mempertahankan dan mengambil pendaftar:

@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. Kesimpulan

Kami telah menunjukkan bahwa dengan beberapa layanan Spring dasar, cukup mudah untuk memasukkan Couchbase ke dalam aplikasi Spring dan mengimplementasikan lapisan persistensi dasar tanpa menggunakan Spring Data.

Kode sumber yang ditunjukkan dalam tutorial ini tersedia di proyek GitHub.

Anda dapat mempelajari lebih lanjut tentang Couchbase Java SDK di situs dokumentasi resmi pengembang Couchbase.