Tutorial: Membangun aplikasi web Java menggunakan Azure Cosmos DB dan API untuk NoSQL

BERLAKU UNTUK: NoSQL

Tutorial aplikasi web Java ini menunjukkan kepada Anda cara menggunakan layanan Microsoft Azure Cosmos DB untuk menyimpan dan mengakses data dari aplikasi Java yang dihosting di Azure App Service Web Apps. Tanpa kartu kredit atau langganan Azure, Anda dapat menyiapkan Coba akun Azure Cosmos DB gratis. Dalam artikel ini, Anda akan mempelajari:

  • Cara membangun aplikasi JavaServer Pages (JSP) dasar di Eclipse.
  • Cara menggunakan layanan Azure Cosmos DB menggunakan Azure Cosmos DB Java SDK.

Tutorial aplikasi Java ini menunjukkan kepada Anda cara membuat aplikasi manajemen tugas berbasis web yang memungkinkan Anda membuat, mengambil, dan menandai tugas sebagai selesai, seperti yang ditunjukkan pada gambar berikut. Setiap tugas dalam daftar ToDo disimpan sebagai dokumen JSON di Azure Cosmos DB.

Aplikasi My ToDo List Java

Tip

Tutorial pengembangan aplikasi ini mengasumsikan bahwa Anda memiliki pengalaman sebelumnya menggunakan Java. Jika Anda baru menggunakan Java atau alat prasyarat, sebaiknya unduh proyek todo lengkap dari GitHub dan membangunnya menggunakan petunjuk di akhir artikel ini. Setelah membuatnya, Anda dapat meninjau artikel untuk mendapatkan wawasan tentang kode dalam konteks proyek.

Prasyarat untuk tutorial aplikasi web Java ini

Sebelum memulai tutorial pengembangan aplikasi ini, Anda harus memiliki hal berikut:

Jika Anda menginstal alat ini untuk pertama kalinya, coreservlets.com menyediakan panduan proses penginstalan di bagian mulai cepat di artikel Tutorial: Menginstal TomCat7 dan Menggunakannya dengan Eclipse.

Membuat akun Azure Cosmos DB

Mari kita mulai dengan membuat akun Azure Cosmos DB. Jika Anda sudah memiliki akun atau jika Anda menggunakan Azure Cosmos DB Emulator untuk tutorial ini, Anda dapat melewati ke Langkah 2: Membuat aplikasi Java JSP.

  1. Dari menu portal Microsoft Azure atau Beranda, pilih Buat sumber daya.

  2. Cari Azure Cosmos DB. Pilih Buat>Azure Cosmos DB.

  3. Pada halaman Buat akun Azure Cosmos DB , pilih opsi Buat di dalam azure Cosmos DB untuk NoSQL .

    Azure Cosmos DB menyediakan beberapa API:

    • NoSQL, untuk data dokumen
    • PostgreSQL
    • MongoDB, untuk data dokumen
    • Apache Cassandra
    • Tabel
    • Apache Gremlin, untuk data grafik

    Untuk mempelajari selengkapnya tentang API untuk NoSQL, lihat Selamat Datang di Azure Cosmos DB.

  4. Di halaman Buat Akun Azure Cosmos DB , masukkan pengaturan dasar untuk akun Azure Cosmos DB baru.

    Pengaturan Nilai Deskripsi
    Langganan Nama langganan Pilih langganan Azure yang ingin Anda gunakan untuk akun Azure Cosmos DB ini.
    Grup Sumber Daya Nama grup sumber daya Pilih grup sumber daya, atau pilih Buat baru, lalu masukkan nama unik untuk grup sumber daya baru.
    Nama Akun Nama yang unik Masukkan nama untuk mengidentifikasi akun Azure Cosmos DB Anda. Karena documents.azure.com ditambahkan ke nama yang Anda berikan untuk membuat URI Anda, gunakan nama yang unik. Nama hanya boleh berisi huruf kecil, angka, dan karakter tanda hubung (-). Nama harus 3-44 karakter.
    Lokasi Wilayah yang paling dekat dengan pengguna Anda Pilih lokasi geografis untuk menghosting akun Azure Cosmos DB Anda. Gunakan lokasi yang paling dekat dengan pengguna Anda untuk memberi mereka akses tercepat ke data.
    Mode kapasitas Throughput yang disediakan atau Tanpa server Pilih Throughput yang disediakan untuk membuat akun dalam mode throughput yang disediakan. Pilih Tanpa server untuk membuat akun dalam mode tanpa server.
    Terapkan diskon tingkat gratis Azure Cosmos DB Terapkan atau Jangan terapkan Dengan Azure Cosmos DB tingkat gratis, Anda mendapatkan 1000 RU/d pertama dan penyimpanan 25 GB secara gratis untuk satu akun. Pelajari lebih lanjut tentang tingkat gratis.
    Membatasi throughput akun total Dipilih atau tidak Batasi jumlah total throughput yang dapat disediakan di akun ini. Batas ini mencegah biaya tak terduga yang terkait dengan throughput yang disediakan. Anda dapat memperbarui atau menghapus batas ini setelah akun Anda dibuat.

    Anda dapat memiliki hingga satu akun Azure Cosmos DB tingkat gratis per langganan Azure dan harus ikut serta saat membuat akun. Jika Anda tidak melihat opsi untuk menerapkan diskon tingkat gratis, akun lain dalam langganan telah diaktifkan dengan tingkat gratis.

    Cuplikan layar memperlihatkan halaman Buat Akun Azure Cosmos DB.

    Catatan

    Opsi berikut ini tidak tersedia jika Anda memilih Tanpa Server sebagai mode Kapasitas:

    • Terapkan Diskon Tingkat Gratis
    • Membatasi throughput akun total
  5. Pada tab Distribusi Global, konfigurasikan detail berikut. Anda dapat menggunakan nilai default untuk mulai cepat ini:

    Pengaturan Nilai Deskripsi
    Redundansi Geografis Nonaktifkan Aktifkan atau nonaktifkan distribusi global di akun Anda dengan memasangkan wilayah Anda dengan wilayah pasangan. Anda dapat menambahkan lebih banyak wilayah ke akun Anda nanti.
    Tulisan Multiwilayah Nonaktifkan Kemampuan tulisan multiwilayah memungkinkan Anda untuk mengambil keuntungan dari throughput terprovisi untuk database dan kontainer Anda di seluruh dunia.
    Zona Ketersediaan Nonaktifkan Zona Ketersediaan membantu Anda lebih meningkatkan ketersediaan dan ketahanan aplikasi Anda.

    Catatan

    Opsi berikut ini tidak tersedia jika Anda memilih Tanpa Server sebagai mode Kapasitas di halaman Dasar sebelumnya:

    • Redundansi Geografis
    • Tulisan Multiwilayah
  6. Secara opsional, Anda dapat mengonfigurasi detail selengkapnya di tab berikut:

    • Jaringan. Mengonfigurasi akses dari jaringan virtual.
    • Kebijakan Pencadangan. Konfigurasikan kebijakan pencadangan berkala atau berkelanjutan .
    • Enkripsi. Gunakan kunci yang dikelola layanan atau kunci yang dikelola pelanggan.
    • Tag. Tag adalah pasangan nama/nilai yang memungkinkan Anda mengategorikan sumber daya dan melihat penagihan gabungan dengan menerapkan tag yang sama ke beberapa sumber daya dan grup sumber daya.
  7. Pilih Tinjau + buat.

  8. Ulas pengaturan akun, lalu pilih Buat. Dibutuhkan beberapa menit untuk membuat akun. Tunggu halaman portal untuk menampilkan Penyebaran Anda selesai.

    Cuplikan layar menunjukkan bahwa penyebaran Anda selesai.

  9. Pilih Buka sumber daya untuk masuk ke halaman akun Azure Cosmos DB.

    Cuplikan layar memperlihatkan halaman akun Azure Cosmos DB.

Buka halaman akun Azure Cosmos DB, dan pilih Kunci. Salin nilai yang akan digunakan dalam aplikasi web yang Anda buat berikutnya.

Cuplikan layar portal Microsoft Azure dengan tombol Kunci disorot di halaman akun Azure Cosmos DB

Membuat aplikasi Java JSP

Untuk membuat aplikasi JSP:

  1. Pertama, kami akan memulai dengan membuat proyek Java. Mulai Eclipse, lalu pilih File, pilih Baru, kemudian pilih Proyek Web Dinamis. Jika Anda tidak melihat Proyek Web Dinamis yang tercantum sebagai proyek yang tersedia, lakukan hal berikut:pilih File, pilih Baru, pilihProyek..., perluas Web, pilih Proyek Web Dinamis, lalu pilih Berikutnya.

    Pengembangan Aplikasi JSP Java

  2. Masukkan nama proyek dalam kotak Nama proyek, dan di menu drop-down Runtime Target, pilih nilai secara opsional (misalnya Apache Tomcat v7.0), lalu klik Selesai. Memilih runtime target memungkinkan Anda menjalankan proyek secara lokal melalui Eclipse.

  3. Di Eclipse, dalam tampilan Project Explorer, perluas proyek Anda. Klik kanan WebContent, klik Baru, lalu klik File JSP.

  4. Dalam kotak dialog File JSP Baru, beri nama file index.jsp. Pertahankan folder induk sebagai WebContent, seperti yang ditampilkan dalam ilustrasi berikut, lalu pilihBerikutnya.

    Membuat File JSP Baru - Tutorial Aplikasi Web Java

  5. Dalam kotak dialog Pilih Templat JSP, untuk tujuan tutorial ini, pilih File JSP Baru (html), lalu pilihSelesai.

  6. Ketika file index.jsp terbuka di Eclipse, tambahkan teks untuk menampilkan Halo Dunia! di dalam elemen <body> yang ada. Konten yang <body> diperbarui akan terlihat seperti kode berikut:

    <body>
      <% out.println("Hello World!"); %>
    </body>
    
  7. Simpan file index.js.

  8. Jika menetapkan runtime target di langkah 2, Anda dapat memilih Proyek, lalu Jalankan untuk menjalankan aplikasi JSP Anda secara lokal:

    Halo Dunia – Tutorial Aplikasi Java

Menginstal SQL Java SDK

Cara termudah untuk menarik SQL Java SDK dan dependensinya adalah melalui Apache Maven. Untuk melakukannya, Anda perlu mengonversi proyek Anda menjadi proyek Maven dengan mengikuti langkah-langkah berikut:

  1. Klik kanan proyek Anda di Project Explorer, pilih Konfigurasikan, pilih Konversi ke Proyek Maven.

  2. Di jendela Buat POM baru, terima default, dan pilih Selesai.

  3. Di Project Explorer, buka file pom.xml.

  4. Pada tab Dependensi, di panel Dependensi, pilih Tambahkan.

  5. Di jendela Pilih Dependensi, lakukan hal berikut ini:

    • Dalam kotak Id Grup, masukkan com.azure.
    • Dalam kotak Id Artefak, masukkan azure-cosmos.
    • Dalam kotak Versi, masukkan 4.11.0.

    Atau, Anda dapat menambahkan XML dependensi untuk ID Grup dan ID Artefak langsung ke file pom.xml:

    <dependency>
      <groupId>com.azure</groupId>
      <artifactId>azure-cosmos</artifactId>
      <version>4.11.0</version>
    </dependency>
    
  6. Pilih OK dan Maven akan menginstal SQL Java SDK atau menyimpan file pom.xml.

Menggunakan layanan Azure Cosmos DB di aplikasi Java Anda

Sekarang mari tambahkan model, tampilan, dan pengontrol ke aplikasi web Anda.

Menambahkan model

Pertama, mari definisikan model dalam file baru TodoItem.java. Kelas TodoItem mendefinisikan skema item bersama dengan metode getter dan setter:

package com.microsoft.azure.cosmos.sample.model;

//@Data
//@Builder
public class TodoItem {
    private String entityType;
    private String category;
    private boolean complete;
    private String id;
    private String name;

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getEntityType() {
        return entityType;
    }

    public void setEntityType(String entityType) {
        this.entityType = entityType;
    }

    public boolean isComplete() {
        return complete;
    }

    public void setComplete(boolean complete) {
        this.complete = complete;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    
}

Tambahkan kelas Objek Akses Data (DAO)

Buat Objek Akses Data (DAO) untuk mengabstraksi item ToDo yang ada ke Azure Cosmos DB. Untuk menyimpan item ToDo ke koleksi, klien perlu mengetahui database dan koleksi mana yang harus dipertahankan (seperti yang direferensikan oleh tautan mandiri). Secara umum, sebaiknya simpan database dan koleksi di dalam cache jika memungkinkan untuk menghindari round-trip tambahan ke database.

  1. Untuk memanggil layanan Azure Cosmos DB, Anda harus membuat objek cosmosClient baru. Secara umum, sebaiknya gunakan kembali objek cosmosClient daripada membangun klien baru untuk setiap permintaan berikutnya. Anda dapat menggunakan kembali klien dengan mendefinisikannya dalam kelas cosmosClientFactory. Perbarui HOST dan MASTER_KEY yang Anda simpan di langkah 1. Ganti variabel HOST dengan URI Anda dan ganti MASTER_KEY dengan PRIMARY KEY Anda. Gunakan kode berikut untuk membuat kelas CosmosClientFactory dalam file CosmosClientFactory.java:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import com.azure.cosmos.ConsistencyLevel;
    import com.azure.cosmos.CosmosClient;
    import com.azure.cosmos.CosmosClientBuilder;
    
    public class CosmosClientFactory {
        private static final String HOST = "[ACCOUNT HOST NAME]";
        private static final String MASTER_KEY = "[ACCOUNT KEY]";
    
        private static CosmosClient cosmosClient = new CosmosClientBuilder()
                .endpoint(HOST)
                .key(MASTER_KEY)
                .consistencyLevel(ConsistencyLevel.EVENTUAL)
                .buildClient();
    
        public static CosmosClient getCosmosClient() {
            return cosmosClient;
        }
    
    }
    
  2. Buat file TodoDao.java dan tambahkan kelas TodoDao untuk membuat, memperbarui, membaca, dan menghapus item todo:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import java.util.List;
    
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    public interface TodoDao {
        /**
         * @return A list of TodoItems
         */
        public List<TodoItem> readTodoItems();
    
        /**
         * @param todoItem
         * @return whether the todoItem was persisted.
         */
        public TodoItem createTodoItem(TodoItem todoItem);
    
        /**
         * @param id
         * @return the TodoItem
         */
        public TodoItem readTodoItem(String id);
    
        /**
         * @param id
         * @return the TodoItem
         */
        public TodoItem updateTodoItem(String id, boolean isComplete);
    
        /**
         *
         * @param id
         * @return whether the delete was successful.
         */
        public boolean deleteTodoItem(String id);
    }
    
  3. Buat file MockDao.java baru dan tambahkan kelas MockDao, kelas ini mengimplementasikan kelas TodoDao untuk melakukan operasi CRUD pada item:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    import lombok.NonNull;
    
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    public class MockDao implements TodoDao {
        private final Map<String, TodoItem> todoItemMap;
    
        public MockDao() {
            todoItemMap = new HashMap<String, TodoItem>();
        }
    
        @Override
        public TodoItem createTodoItem(@NonNull TodoItem todoItem) {
            if (todoItem.getId() == null || todoItem.getId().isEmpty()) {
                todoItem.setId(generateId());
            }
            todoItemMap.put(todoItem.getId(), todoItem);
            return todoItem;
        }
    
        @Override
        public TodoItem readTodoItem(@NonNull String id) {
            return todoItemMap.get(id);
        }
    
        @Override
        public List<TodoItem> readTodoItems() {
            return new ArrayList<TodoItem>(todoItemMap.values());
        }
    
        @Override
        public TodoItem updateTodoItem(String id, boolean isComplete) {
            todoItemMap.get(id).setComplete(isComplete);
            return todoItemMap.get(id);
        }
    
        @Override
        public boolean deleteTodoItem(@NonNull String id) {
            todoItemMap.remove(id);
            return true;
        }
    
        private String generateId() {
            return new Integer(todoItemMap.size()).toString();
        }
    }
    
  4. Buat file DocDbDao.java baru dan tambahkan kelas DocDbDao. Kelas ini mendefinisikan kode untuk bertahan pada TodoItems ke dalam kontainer, mengambil database dan koleksi Anda, jika ada, atau membuat yang baru jika tidak ada. Contoh ini menggunakan Gson untuk membuat serialisasi dan membatalkan serialisasi TodoItem Plain Old Java Objects (POJOs) ke dokumen JSON. Untuk menyimpan item ToDo ke koleksi, klien perlu mengetahui database dan koleksi mana yang harus dipertahankan (seperti yang direferensikan oleh tautan mandiri). Kelas ini juga mendefinisikan fungsi bantuan untuk mengambil dokumen dengan atribut lain (misalnya "ID") daripada tautan mandiri. Anda dapat menggunakan metode pembantu untuk mengambil dokumen TodoItem JSON dengan ID dan kemudian membatalkan serialisasinya ke POJO.

    Anda juga dapat menggunakan objek klien cosmosClient untuk mendapatkan koleksi atau daftar TodoItems menggunakan kueri SQL. Terakhir, Anda menentukan metode penghapusan untuk menghapus TodoItem dari daftar Anda. Kode berikut menunjukkan konten kelas DocDbDao:

    package com.microsoft.azure.cosmos.sample.dao;
    
    import com.azure.cosmos.CosmosClient;
    import com.azure.cosmos.CosmosContainer;
    import com.azure.cosmos.CosmosDatabase;
    import com.azure.cosmos.CosmosException;
    import com.azure.cosmos.implementation.Utils;
    import com.azure.cosmos.models.CosmosContainerProperties;
    import com.azure.cosmos.models.CosmosContainerResponse;
    import com.azure.cosmos.models.CosmosDatabaseResponse;
    import com.azure.cosmos.models.CosmosItemRequestOptions;
    import com.azure.cosmos.models.CosmosQueryRequestOptions;
    import com.azure.cosmos.models.FeedResponse;
    import com.azure.cosmos.models.PartitionKey;
    import com.fasterxml.jackson.core.JsonProcessingException;
    import com.fasterxml.jackson.databind.JsonNode;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.databind.node.ObjectNode;
    import com.google.gson.Gson;
    import com.microsoft.azure.cosmos.sample.model.TodoItem;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class DocDbDao implements TodoDao {
        // The name of our database.
        private static final String DATABASE_ID = "TestDB";
    
        // The name of our collection.
        private static final String CONTAINER_ID = "TestCollection";
    
        // We'll use Gson for POJO <=> JSON serialization for this example.
        private static Gson gson = new Gson();
    
        // The Cosmos DB Client
        private static CosmosClient cosmosClient = CosmosClientFactory
            .getCosmosClient();
    
        // The Cosmos DB database
        private static CosmosDatabase cosmosDatabase = null;
    
        // The Cosmos DB container
        private static CosmosContainer cosmosContainer = null;
    
        // For POJO/JsonNode interconversion
        private static final ObjectMapper OBJECT_MAPPER = Utils.getSimpleObjectMapper();
    
        @Override
        public TodoItem createTodoItem(TodoItem todoItem) {
            // Serialize the TodoItem as a JSON Document.
    
            JsonNode todoItemJson = OBJECT_MAPPER.valueToTree(todoItem);
    
            ((ObjectNode) todoItemJson).put("entityType", "todoItem");
    
            try {
                // Persist the document using the DocumentClient.
                todoItemJson =
                    getContainerCreateResourcesIfNotExist()
                        .createItem(todoItemJson)
                        .getItem();
            } catch (CosmosException e) {
                System.out.println("Error creating TODO item.\n");
                e.printStackTrace();
                return null;
            }
    
    
            try {
    
                return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
                //return todoItem;
            } catch (Exception e) {
                System.out.println("Error deserializing created TODO item.\n");
                e.printStackTrace();
    
                return null;
            }
    
        }
    
        @Override
        public TodoItem readTodoItem(String id) {
            // Retrieve the document by id using our helper method.
            JsonNode todoItemJson = getDocumentById(id);
    
            if (todoItemJson != null) {
                // De-serialize the document in to a TodoItem.
                try {
                    return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
                } catch (JsonProcessingException e) {
                    System.out.println("Error deserializing read TODO item.\n");
                    e.printStackTrace();
    
                    return null;
                }
            } else {
                return null;
            }
        }
    
        @Override
        public List<TodoItem> readTodoItems() {
    
            List<TodoItem> todoItems = new ArrayList<TodoItem>();
    
            String sql = "SELECT * FROM root r WHERE r.entityType = 'todoItem'";
            int maxItemCount = 1000;
            int maxDegreeOfParallelism = 1000;
            int maxBufferedItemCount = 100;
    
            CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
            options.setMaxBufferedItemCount(maxBufferedItemCount);
            options.setMaxDegreeOfParallelism(maxDegreeOfParallelism);
            options.setQueryMetricsEnabled(false);
    
            int error_count = 0;
            int error_limit = 10;
    
            String continuationToken = null;
            do {
    
                for (FeedResponse<JsonNode> pageResponse :
                    getContainerCreateResourcesIfNotExist()
                        .queryItems(sql, options, JsonNode.class)
                        .iterableByPage(continuationToken, maxItemCount)) {
    
                    continuationToken = pageResponse.getContinuationToken();
    
                    for (JsonNode item : pageResponse.getElements()) {
    
                        try {
                            todoItems.add(OBJECT_MAPPER.treeToValue(item, TodoItem.class));
                        } catch (JsonProcessingException e) {
                            if (error_count < error_limit) {
                                error_count++;
                                if (error_count >= error_limit) {
                                    System.out.println("\n...reached max error count.\n");
                                } else {
                                    System.out.println("Error deserializing TODO item JsonNode. " +
                                        "This item will not be returned.");
                                    e.printStackTrace();
                                }
                            }
                        }
    
                    }
                }
    
            } while (continuationToken != null);
    
            return todoItems;
        }
    
        @Override
        public TodoItem updateTodoItem(String id, boolean isComplete) {
            // Retrieve the document from the database
            JsonNode todoItemJson = getDocumentById(id);
    
            // You can update the document as a JSON document directly.
            // For more complex operations - you could de-serialize the document in
            // to a POJO, update the POJO, and then re-serialize the POJO back in to
            // a document.
            ((ObjectNode) todoItemJson).put("complete", isComplete);
    
            try {
                // Persist/replace the updated document.
                todoItemJson =
                    getContainerCreateResourcesIfNotExist()
                        .replaceItem(todoItemJson, id, new PartitionKey(id), new CosmosItemRequestOptions())
                        .getItem();
            } catch (CosmosException e) {
                System.out.println("Error updating TODO item.\n");
                e.printStackTrace();
                return null;
            }
    
            // De-serialize the document in to a TodoItem.
            try {
                return OBJECT_MAPPER.treeToValue(todoItemJson, TodoItem.class);
            } catch (JsonProcessingException e) {
                System.out.println("Error deserializing updated item.\n");
                e.printStackTrace();
    
                return null;
            }
        }
    
        @Override
        public boolean deleteTodoItem(String id) {
            // CosmosDB refers to documents by self link rather than id.
    
            // Query for the document to retrieve the self link.
            JsonNode todoItemJson = getDocumentById(id);
    
            try {
                // Delete the document by self link.
                getContainerCreateResourcesIfNotExist()
                    .deleteItem(id, new PartitionKey(id), new CosmosItemRequestOptions());
            } catch (CosmosException e) {
                System.out.println("Error deleting TODO item.\n");
                e.printStackTrace();
                return false;
            }
    
            return true;
        }
    
        /*
        
        private CosmosDatabase getTodoDatabase() {
            if (databaseCache == null) {
                // Get the database if it exists
                List<CosmosDatabase> databaseList = cosmosClient
                        .queryDatabases(
                                "SELECT * FROM root r WHERE r.id='" + DATABASE_ID
                                        + "'", null).getQueryIterable().toList();
    
                if (databaseList.size() > 0) {
                    // Cache the database object so we won't have to query for it
                    // later to retrieve the selfLink.
                    databaseCache = databaseList.get(0);
                } else {
                    // Create the database if it doesn't exist.
                    try {
                        CosmosDatabase databaseDefinition = new CosmosDatabase();
                        databaseDefinition.setId(DATABASE_ID);
    
                        databaseCache = cosmosClient.createDatabase(
                                databaseDefinition, null).getResource();
                    } catch (CosmosException e) {
                        // TODO: Something has gone terribly wrong - the app wasn't
                        // able to query or create the collection.
                        // Verify your connection, endpoint, and key.
                        e.printStackTrace();
                    }
                }
            }
    
            return databaseCache;
        }
    
        */
    
        private CosmosContainer getContainerCreateResourcesIfNotExist() {
    
            try {
    
                if (cosmosDatabase == null) {
                    CosmosDatabaseResponse cosmosDatabaseResponse = cosmosClient.createDatabaseIfNotExists(DATABASE_ID);
                    cosmosDatabase = cosmosClient.getDatabase(cosmosDatabaseResponse.getProperties().getId());
                }
    
            } catch (CosmosException e) {
                // TODO: Something has gone terribly wrong - the app wasn't
                // able to query or create the collection.
                // Verify your connection, endpoint, and key.
                System.out.println("Something has gone terribly wrong - " +
                    "the app wasn't able to create the Database.\n");
                e.printStackTrace();
            }
    
            try {
    
                if (cosmosContainer == null) {
                    CosmosContainerProperties properties = new CosmosContainerProperties(CONTAINER_ID, "/id");
                    CosmosContainerResponse cosmosContainerResponse = cosmosDatabase.createContainerIfNotExists(properties);
                    cosmosContainer = cosmosDatabase.getContainer(cosmosContainerResponse.getProperties().getId());
                }
    
            } catch (CosmosException e) {
                // TODO: Something has gone terribly wrong - the app wasn't
                // able to query or create the collection.
                // Verify your connection, endpoint, and key.
                System.out.println("Something has gone terribly wrong - " +
                    "the app wasn't able to create the Container.\n");
                e.printStackTrace();
            }
    
            return cosmosContainer;
        }
    
        private JsonNode getDocumentById(String id) {
    
            String sql = "SELECT * FROM root r WHERE r.id='" + id + "'";
            int maxItemCount = 1000;
            int maxDegreeOfParallelism = 1000;
            int maxBufferedItemCount = 100;
    
            CosmosQueryRequestOptions options = new CosmosQueryRequestOptions();
            options.setMaxBufferedItemCount(maxBufferedItemCount);
            options.setMaxDegreeOfParallelism(maxDegreeOfParallelism);
            options.setQueryMetricsEnabled(false);
    
            List<JsonNode> itemList = new ArrayList();
    
            String continuationToken = null;
            do {
                for (FeedResponse<JsonNode> pageResponse :
                    getContainerCreateResourcesIfNotExist()
                        .queryItems(sql, options, JsonNode.class)
                        .iterableByPage(continuationToken, maxItemCount)) {
    
                    continuationToken = pageResponse.getContinuationToken();
    
                    for (JsonNode item : pageResponse.getElements()) {
                        itemList.add(item);
                    }
                }
    
            } while (continuationToken != null);
    
            if (itemList.size() > 0) {
                return itemList.get(0);
            } else {
                return null;
            }
        }
    
    }
    
  5. Selanjutnya, buat file TodoDaoFactory.java baru dan tambahkan kelas TodoDaoFactory yang akan membuat objek DocDbDao baru:

    package com.microsoft.azure.cosmos.sample.dao;
    
    public class TodoDaoFactory {
        private static TodoDao myTodoDao = new DocDbDao();
    
        public static TodoDao getDao() {
            return myTodoDao;
        }
    }
    

Menambahkan pengontrol

Tambahkan pengontrol TodoItemController ke aplikasi Anda. Dalam proyek ini, Anda menggunakan Project Lombok untuk menghasilkan konstruktor, getter, setter, dan penyusun. Atau, Anda dapat menulis kode ini secara manual atau meminta IDE untuk membuatnya.:

package com.microsoft.azure.cosmos.sample.controller;

import java.util.List;
import java.util.UUID;

import lombok.NonNull;

import com.microsoft.azure.cosmos.sample.dao.TodoDao;
import com.microsoft.azure.cosmos.sample.dao.TodoDaoFactory;
import com.microsoft.azure.cosmos.sample.model.TodoItem;

public class TodoItemController {
    public static TodoItemController getInstance() {
        if (todoItemController == null) {
            todoItemController = new TodoItemController(TodoDaoFactory.getDao());
        }
        return todoItemController;
    }

    private static TodoItemController todoItemController;

    private final TodoDao todoDao;

    TodoItemController(TodoDao todoDao) {
        this.todoDao = todoDao;
    }

    public TodoItem createTodoItem(@NonNull String name,
            @NonNull String category, boolean isComplete) {
        TodoItem todoItem = new TodoItem();
        
        todoItem.setName(name);
        todoItem.setCategory(category);
        todoItem.setComplete(isComplete);
        todoItem.setId(UUID.randomUUID().toString());

        return todoDao.createTodoItem(todoItem);
    }

    public boolean deleteTodoItem(@NonNull String id) {
        return todoDao.deleteTodoItem(id);
    }

    public TodoItem getTodoItemById(@NonNull String id) {
        return todoDao.readTodoItem(id);
    }

    public List<TodoItem> getTodoItems() {
        return todoDao.readTodoItems();
    }

    public TodoItem updateTodoItem(@NonNull String id, boolean isComplete) {
        return todoDao.updateTodoItem(id, isComplete);
    }
}

Membuat servlet

Selanjutnya, buat servlet untuk merutekan permintaan HTTP ke pengontrol. Buat file ApiServlet.java dan tentukan kode berikut di bawahnya:

package com.microsoft.azure.cosmos.sample;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.gson.Gson;
import com.microsoft.azure.cosmos.sample.controller.TodoItemController;

/**
 * API Frontend Servlet
 */
@WebServlet("/api")
public class ApiServlet extends HttpServlet {
    // API Keys
    public static final String API_METHOD = "method";

    // API Methods
    public static final String CREATE_TODO_ITEM = "createTodoItem";
    public static final String GET_TODO_ITEMS = "getTodoItems";
    public static final String UPDATE_TODO_ITEM = "updateTodoItem";

    // API Parameters
    public static final String TODO_ITEM_ID = "todoItemId";
    public static final String TODO_ITEM_NAME = "todoItemName";
    public static final String TODO_ITEM_CATEGORY = "todoItemCategory";
    public static final String TODO_ITEM_COMPLETE = "todoItemComplete";

    public static final String MESSAGE_ERROR_INVALID_METHOD = "{'error': 'Invalid method'}";

    private static final long serialVersionUID = 1L;
    private static final Gson gson = new Gson();

    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        String apiResponse = MESSAGE_ERROR_INVALID_METHOD;

        TodoItemController todoItemController = TodoItemController
                .getInstance();

        String id = request.getParameter(TODO_ITEM_ID);
        String name = request.getParameter(TODO_ITEM_NAME);
        String category = request.getParameter(TODO_ITEM_CATEGORY);
        String itemComplete = request.getParameter(TODO_ITEM_COMPLETE);
        boolean isComplete = itemComplete!= null && itemComplete.equalsIgnoreCase("true");

        switch (request.getParameter(API_METHOD)) {
        case CREATE_TODO_ITEM:
            apiResponse = gson.toJson(todoItemController.createTodoItem(name,
                    category, isComplete));
            break;
        case GET_TODO_ITEMS:
            apiResponse = gson.toJson(todoItemController.getTodoItems());
            break;
        case UPDATE_TODO_ITEM:
            apiResponse = gson.toJson(todoItemController.updateTodoItem(id,
                    isComplete));
            break;
        default:
            break;
        }

        response.setCharacterEncoding("UTF-8");
        response.getWriter().println(apiResponse);
    }

    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

Sambungkan sisa aplikasi Java bersama-sama

Setelah menyelesaikan langkah yang menyenangkan, sisanya adalah membangun antarmuka pengguna yang cepat dan menyambungkannya ke DAO Anda.

  1. Anda memerlukan antarmuka pengguna web untuk ditampilkan kepada pengguna. Mari tulis ulang index.js yang dibuat sebelumnya dengan kode berikut:

    <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge;" />
      <title>Azure Cosmos Java Sample</title>
    
      <!-- Bootstrap -->
      <link href="//ajax.aspnetcdn.com/ajax/bootstrap/3.2.0/css/bootstrap.min.css" rel="stylesheet">
    
      <style>
        /* Add padding to body for fixed nav bar */
        body {
          padding-top: 50px;
        }
      </style>
    </head>
    <body>
      <!-- Nav Bar -->
      <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
        <div class="container">
          <div class="navbar-header">
            <a class="navbar-brand" href="#">My Tasks</a>
          </div>
        </div>
      </div>
    
      <!-- Body -->
      <div class="container">
        <h1>My ToDo List</h1>
    
        <hr/>
    
        <!-- The ToDo List -->
        <div class = "todoList">
          <table class="table table-bordered table-striped" id="todoItems">
            <thead>
              <tr>
                <th>Name</th>
                <th>Category</th>
                <th>Complete</th>
              </tr>
            </thead>
            <tbody>
            </tbody>
          </table>
    
          <!-- Update Button -->
          <div class="todoUpdatePanel">
            <form class="form-horizontal" role="form">
              <button type="button" class="btn btn-primary">Update Tasks</button>
            </form>
          </div>
    
        </div>
    
        <hr/>
    
        <!-- Item Input Form -->
        <div class="todoForm">
          <form class="form-horizontal" role="form">
            <div class="form-group">
              <label for="inputItemName" class="col-sm-2">Task Name</label>
              <div class="col-sm-10">
                <input type="text" class="form-control" id="inputItemName" placeholder="Enter name">
              </div>
            </div>
    
            <div class="form-group">
              <label for="inputItemCategory" class="col-sm-2">Task Category</label>
              <div class="col-sm-10">
                <input type="text" class="form-control" id="inputItemCategory" placeholder="Enter category">
              </div>
            </div>
    
            <button type="button" class="btn btn-primary">Add Task</button>
          </form>
        </div>
    
      </div>
    
      <!-- Placed at the end of the document so the pages load faster -->
      <script src="//ajax.aspnetcdn.com/ajax/jQuery/jquery-2.1.1.min.js"></script>
      <script src="//ajax.aspnetcdn.com/ajax/bootstrap/3.2.0/bootstrap.min.js"></script>
      <script src="assets/todo.js"></script>
    </body>
    </html>
    
  2. Akhirnya, tulis beberapa JavaScript sisi klien untuk mengikat antarmuka pengguna web dan servlet bersama-sama:

    /**
     * ToDo App
     */
    
    var todoApp = {
      /*
       * API methods to call Java backend.
       */
      apiEndpoint: "api",
    
      createTodoItem: function(name, category, isComplete) {
        $.post(todoApp.apiEndpoint, {
            "method": "createTodoItem",
            "todoItemName": name,
            "todoItemCategory": category,
            "todoItemComplete": isComplete
          },
          function(data) {
            var todoItem = data;
            todoApp.addTodoItemToTable(todoItem.id, todoItem.name, todoItem.category, todoItem.complete);
          },
          "json");
      },
    
      getTodoItems: function() {
        $.post(todoApp.apiEndpoint, {
            "method": "getTodoItems"
          },
          function(data) {
            var todoItemArr = data;
            $.each(todoItemArr, function(index, value) {
              todoApp.addTodoItemToTable(value.id, value.name, value.category, value.complete);
            });
          },
          "json");
      },
    
      updateTodoItem: function(id, isComplete) {
        $.post(todoApp.apiEndpoint, {
            "method": "updateTodoItem",
            "todoItemId": id,
            "todoItemComplete": isComplete
          },
          function(data) {},
          "json");
      },
    
      /*
       * UI Methods
       */
      addTodoItemToTable: function(id, name, category, isComplete) {
        var rowColor = isComplete ? "active" : "warning";
    
        todoApp.ui_table().append($("<tr>")
          .append($("<td>").text(name))
          .append($("<td>").text(category))
          .append($("<td>")
            .append($("<input>")
              .attr("type", "checkbox")
              .attr("id", id)
              .attr("checked", isComplete)
              .attr("class", "isComplete")
            ))
          .addClass(rowColor)
        );
      },
    
      /*
       * UI Bindings
       */
      bindCreateButton: function() {
        todoApp.ui_createButton().click(function() {
          todoApp.createTodoItem(todoApp.ui_createNameInput().val(), todoApp.ui_createCategoryInput().val(), false);
          todoApp.ui_createNameInput().val("");
          todoApp.ui_createCategoryInput().val("");
        });
      },
    
      bindUpdateButton: function() {
        todoApp.ui_updateButton().click(function() {
          // Disable button temporarily.
          var myButton = $(this);
          var originalText = myButton.text();
          $(this).text("Updating...");
          $(this).prop("disabled", true);
    
          // Call api to update todo items.
          $.each(todoApp.ui_updateId(), function(index, value) {
            todoApp.updateTodoItem(value.name, value.value);
            $(value).remove();
          });
    
          // Re-enable button.
          setTimeout(function() {
            myButton.prop("disabled", false);
            myButton.text(originalText);
          }, 500);
        });
      },
    
      bindUpdateCheckboxes: function() {
        todoApp.ui_table().on("click", ".isComplete", function(event) {
          var checkboxElement = $(event.currentTarget);
          var rowElement = $(event.currentTarget).parents('tr');
          var id = checkboxElement.attr('id');
          var isComplete = checkboxElement.is(':checked');
    
          // Togle table row color
          if (isComplete) {
            rowElement.addClass("active");
            rowElement.removeClass("warning");
          } else {
            rowElement.removeClass("active");
            rowElement.addClass("warning");
          }
    
          // Update hidden inputs for update panel.
          todoApp.ui_updateForm().children("input[name='" + id + "']").remove();
    
          todoApp.ui_updateForm().append($("<input>")
            .attr("type", "hidden")
            .attr("class", "updateComplete")
            .attr("name", id)
            .attr("value", isComplete));
    
        });
      },
    
      /*
       * UI Elements
       */
      ui_createNameInput: function() {
        return $(".todoForm #inputItemName");
      },
    
      ui_createCategoryInput: function() {
        return $(".todoForm #inputItemCategory");
      },
    
      ui_createButton: function() {
        return $(".todoForm button");
      },
    
      ui_table: function() {
        return $(".todoList table tbody");
      },
    
      ui_updateButton: function() {
        return $(".todoUpdatePanel button");
      },
    
      ui_updateForm: function() {
        return $(".todoUpdatePanel form");
      },
    
      ui_updateId: function() {
        return $(".todoUpdatePanel .updateComplete");
      },
    
      /*
       * Install the TodoApp
       */
      install: function() {
        todoApp.bindCreateButton();
        todoApp.bindUpdateButton();
        todoApp.bindUpdateCheckboxes();
    
        todoApp.getTodoItems();
      }
    };
    
    $(document).ready(function() {
      todoApp.install();
    });
    
  3. Langkah terakhir adalah menguji aplikasi. Jalankan aplikasi secara lokal, dan tambahkan beberapa item Todo dengan mengisi nama dan kategori item, dengan mengklik Tambahkan Tugas. Setelah item muncul, Anda bisa memperbarui apakah item selesai dengan beralih ke kotak centang dan mengklik Perbarui Tugas.

Menyebarkan aplikasi Java Anda ke Situs Web Azure

Situs Web Azure membuat penyebaran aplikasi Java semudah mengekspor aplikasi Anda sebagai file WAR dan mengunggahnya melalui kontrol sumber (misalnya Git) atau FTP.

  1. Untuk mengekspor aplikasi Anda sebagai file WAR, klik kanan pada proyek Anda di Project Explorer, klik Ekspor, lalu klik File WAR.

  2. Di jendela Ekspor WAR, lakukan hal berikut:

    • Dalam kotak proyek Web, masukkan azure-cosmos-java-sample.
    • Dalam kotak Tujuan, pilih tujuan untuk menyimpan file WAR.
    • Pilih Selesai.
  3. Setelah memiliki file WAR, unggah ke direktori webapps Situs web Azure Anda. Untuk mendapatkan petunjuk tentang cara mengunggah file, lihat Menambahkan aplikasi Java ke Azure App Service Web Apps. Setelah file WAR diunggah ke direktori webapps, lingkungan runtime akan mendeteksi bahwa Anda telah menambahkannya dan akan secara otomatis memuatnya.

  4. Untuk melihat produk yang sudah selesai, buka http://YOUR\_SITE\_NAME.azurewebsites.net/azure-cosmos-java-sample/ dan mulai tambahkan tugas Anda!

Mendapatkan proyek dari GitHub

Semua sampel dalam tutorial ini termasuk dalam proyek todo di GitHub. Untuk mengimpor proyek todo ke Eclipse, pastikan Anda memiliki perangkat lunak dan sumber daya yang tercantum di bagian Prasyarat, lalu lakukan hal berikut:

  1. Instal Project Lombok. Lombok digunakan untuk menghasilkan konstruktor, getter, setter dalam proyek. Setelah mengunduh file lombok.jar, klik dua kali untuk menginstalnya atau menginstalnya dari baris perintah.

  2. Jika Eclipse terbuka, tutup dan mulai ulang untuk memuat Lombok.

  3. Di dalam Eclipse, di menuFile, pilih Import.

  4. Di dalam Import window, pilih Git, pilih Proyek dari Git, kemudian pilih Berikutnya.

  5. Pada layar Pilih Sumber Repositori, pilih Kloning URI.

  6. Pada layar Repositori Git Sumber, dalam kotak URI, masukkan https://github.com/Azure-Samples/azure-cosmos-java-sql-api-todo-app, lalu klik Berikutnya.

  7. Pada layar Pilihan Cabang, pastikan bahwa utama dipilih, lalu klik Berikutnya.

  8. Pada layar Tujuan Lokal, klik Telusuri untuk memilih folder tempat repositori dapat disalin, lalu klik Berikutnya.

  9. Pada layar Pilih wizard yang akan digunakan untuk mengimpor proyek, pastikan bahwa opsi Impor proyek yang sudah ada dipilih, lalu klik Berikutnya.

  10. Pada layar Impor Proyek, batalkan pilihan proyek DocumentDB, lalu klik Selesai. Proyek DocumentDB berisi Azure Cosmos DB Java SDK, yang akan kami tambahkan sebagai dependensi sebagai gantinya.

  11. Di Project Explorer, buka azure-cosmos-java-sample\src\com.microsoft.azure.cosmos.sample.dao\DocumentClientFactory.java dan ganti nilai HOST dan MASTER_KEY dengan URI dan PRIMARY KEY untuk akun Azure Cosmos DB Anda, lalu simpan file. Untuk informasi selengkapnya, lihat Langkah 1. Membuat akun database Azure Cosmos DB.

  12. Di Project Explorer, klik kanan azure-cosmos-java-sample, klik Jalur Build, lalu klik Konfigurasikan Jalur Build.

  13. Pada layar Jalur build Java, di panel kanan, pilih tab Pustaka, lalu klik Tambahkan Eksternal JAR. Buka lokasi file lombok.jar, dan klik Buka, lalu klik OK.

  14. Gunakan langkah 12 untuk membuka jendela Properti kembali, lalu di panel kiri, klik Runtime Yang Ditargetkan.

  15. Pada layar Runtimes Yang Ditargetkan, klik Baru, pilih Apache Tomcat v7.0, lalu klik OK.

  16. Gunakan langkah 12 untuk membuka kembali jendela Properti, lalu di panel kiri, klik Faset Proyek.

  17. Pada layar Faset Proyek, pilih Modul Web Dinamis dan Java, lalu klik OK.

  18. Pada tab Server di bagian bawah layar, klik kanan Tomcat v7.0 Server di localhost, lalu klik Tambahkan dan Hapus.

  19. Pada jendela Tambahkan dan Hapus, pindahkan azure-cosmos-java-sample ke kotak Dikonfigurasi, lalu klik Selesai.

  20. Di tab Server, klik kanan Tomcat v7.0 Server di localhost, lalu klik Mulai ulang.

  21. Di browser, buka http://localhost:8080/azure-cosmos-java-sample/ dan mulai tambahkan ke daftar tugas Anda. Perhatikan bahwa jika Anda mengubah nilai port default, ubah 8080 ke nilai yang Anda pilih.

  22. Untuk menyebarkan proyek Anda ke situs web Azure, lihat Langkah 6. Sebarkan aplikasi Anda ke Situs Web Azure.

Langkah berikutnya

Mencoba melakukan perencanaan kapasitas untuk migrasi ke Azure Cosmos DB? Anda dapat menggunakan informasi tentang kluster database Anda yang ada saat ini untuk perencanaan kapasitas.