Phát triển Java 2.0: Lưu trữ đám mây với SimpleDB của Amazon, Phần 2

Việc mô hình hóa các đối tượng miền cho hầu như bất kỳ kiểu ứng dụng nào rất dễ dàng khi sử dụng một khung công tác quan hệ như Grails, nhưng còn về SimpleDB thì sao? Trong phần hai của bài giới thiệu về SimpleDB, Andrew Glover cho bạn thấy cách sử dụng SimpleJPA, chứ không phải là SDK của Amazon, để duy trì các đối tượng trong lưu trữ đám mây của SimpleDB. Ngoài việc cho phép bạn sử dụng các đối tượng Java™ cũ đơn giản (POJO) để mô hình hóa miền (theo JPA - API tồn tại lâu bền của Java), SimpleJPA tự động chuyển đổi các kiểu dữ liệu nguyên thủy thành các chuỗi ký tự thân thiện với Amazon. Thực ra bạn không thể đòi hỏi một cách tiếp cận đơn giản hơn nữa để lưu trữ trên đám mây.

pdf21 trang | Chia sẻ: lylyngoc | Lượt xem: 1645 | Lượt tải: 1download
Bạn đang xem trước 20 trang tài liệu Phát triển Java 2.0: Lưu trữ đám mây với SimpleDB của Amazon, Phần 2, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Phát triển Java 2.0: Lưu trữ đám mây với SimpleDB của Amazon, Phần 2 Sự tồn tại lâu bền của đối tượng cũ đơn giản với SimpleJPA Việc mô hình hóa các đối tượng miền cho hầu như bất kỳ kiểu ứng dụng nào rất dễ dàng khi sử dụng một khung công tác quan hệ như Grails, nhưng còn về SimpleDB thì sao? Trong phần hai của bài giới thiệu về SimpleDB, Andrew Glover cho bạn thấy cách sử dụng SimpleJPA, chứ không phải là SDK của Amazon, để duy trì các đối tượng trong lưu trữ đám mây của SimpleDB. Ngoài việc cho phép bạn sử dụng các đối tượng Java™ cũ đơn giản (POJO) để mô hình hóa miền (theo JPA - API tồn tại lâu bền của Java), SimpleJPA tự động chuyển đổi các kiểu dữ liệu nguyên thủy thành các chuỗi ký tự thân thiện với Amazon. Thực ra bạn không thể đòi hỏi một cách tiếp cận đơn giản hơn nữa để lưu trữ trên đám mây. Trong phần đầu của bài giới thiệu về SimpleDB này, tôi đã giới thiệu cho bạn cách sử dụng API của Amazon để mô hình hóa một ứng dụng chạy đua theo phong cách CRUD. Ngoài sự duy nhất hiển nhiên dành cho hầu hết các nhà phát triển Java về cách tiếp cận chỉ theo chuỗi ký tự của Amazon với các kiểu dữ liệu, bạn có thể như đã thấy chính mình đang xem xét API của Amazon với một chút hoài nghi. Cuối cùng, các API để sử dụng một cơ sở dữ liệu quan hệ bây giờ là khá chuẩn và có căn cứ — và có lẽ quan trọng hơn là chúng đã trở nên quen thuộc rồi. Ở hậu trường, hiện nay nhiều framework quan hệ triển khai các API Java Persistence. Điều này làm cho việc mô hình hóa các đối tượng miền cho hầu hết bất kỳ kiểu ứng dụng Java nào trở nên vừa dễ dàng, vừa quen thuộc trên phạm vi của các RDBMS. Khi bạn đã nắm vững về một mô hình hóa thì việc khó chấp nhận một cách tiếp cận mới về mô hình hóa miền là điều hiển nhiên thôi — và tin tốt là với SimpleDB, bạn không phải làm như vậy. Trong phần 2 này, tôi sẽ hướng dẫn cho bạn cách cấu trúc lại ứng dụng chạy đua từ Phần 1 cho phù hợp với đặc tả JPA. Sau đó, chúng ta sẽ gửi ứng dụng tới SimpleJPA và tìm hiểu một số cách về nền tảng nguồn mở, đổi mới này có thể làm cho thích nghi với việc mô hình hóa miền NoSQL và lưu trữ dựa trên đám mây, trở nên dễ dàng hơn một chút. Hibernate và JPA: Lịch sử tóm tắt Nhiều nhà phát triển Java hiện nay sử dụng Hibernate (và Spring) để duy trì dữ liệu. Ngoài việc là một tín hiệu về sự thành công của nguồn mở, Hibernate đã thay đổi lĩnh vực ORM (Ánh xạ quan hệ-đối tượng) mãi mãi. Trước khi chúng ta có Hibernate, các nhà phát triển Java đã phải đối phó với tình trạng sa lầy của các bean thực thể của EJB (EJB entity beans); trước đó, về cơ bản chúng ta đã triển khai các ORM riêng của mình hoặc đã mua một ORM từ một nhà cung cấp như IBM®. Hibernate đã trút bỏ tất cả sự phức tạp đó và chi phí phải trả cho nền tảng mô hình hóa dựa trên POJO mà nhiều người trong chúng ta coi là đúng hiện nay. Người ta đã tạo ra Java Persistence API (JPA) để đáp ứng với tính phổ biến của sự đổi mới của Hibernate về sử dụng các POJO để mô hình hóa dữ liệu. Hiện nay, EJB 3.0 triển khai thực hiện JPA và do đó, thực hiện cả Google App Engine nữa. Ngay cả bản thân Hibernate là một công cụ JPA, khi giả định bạn sử dụng EntityManager của Hibernate. Cứ cho là các nhà phát triển Java đã bắt đầu thoải mái với việc mô hình hóa các ứng dụng lấy dữ liệu là trung tâm khi sử dụng các POJO, đúng là một kho dữ liệu như SimpleDB nên cung cấp cho chúng ta một lựa chọn tương tự. Cuối cùng chẳng phải nó giống như một cơ sở dữ liệu sao? Mô hình hóa dữ liệu với các đối tượng Để sử dụng SimpleJPA, chúng ta cần làm một chút trên các đối tượng Racer và Runner của mình, đưa chúng lên ngang tầm với đặc tả JPA. May mắn thay, những điều cơ bản của JPA khá đơn giản: bạn gắn các POJO bình thường với các chú thích và việc thực hiện EntityManager sẽ lo nốt phần còn lại — không cần XML nào. JPA sử dụng hai trong số các chú thích chính là @Entity và @Id, chỉ rõ một POJO là tồn tại lâu bền và mô tả khóa nhận dạng của nó, tương ứng. Đối với mục đích chuyển đổi ứng dụng chạy đua của chúng ta sang JPA, chúng ta cũng sẽ cần sử dụng hai chú thích để quản lý các mối quan hệ: @OneToMany và @ManyToOne. Trong nửa đầu của bài này, tôi đã giới thiệu cho bạn cách duy trì những người chạy thi và các cuộc thi chạy. Tôi chưa bao giờ sử dụng bất kỳ các đối tượng nào để đại diện cho các thực thể đó, tuy nhiên — tôi chỉ sử dụng API thô của Amazon để duy trì những đặc tính của cả hai thực thể. Nếu tôi muốn mô hình hóa một mối quan hệ đơn giản giữa một cuộc chạy thi và những người chạy thi của nó, tôi đã có thể làm như thế như trong Liệt kê 1: Liệt kê 1. Một đối tượng Race đơn giản public class Race { private String name; private String location; private double distance; private List runners; //setters and getters left out... } Trong Liệt kê 1, tôi đã chỉ rõ một đối tượng Race có bốn thuộc tính, thuộc tính cuối cùng trong số đó là một Collection (bộ sưu tập) của các runner (người chạy thi). Tiếp theo, tôi có thể tạo ra một đối tượng Runner đơn giản (như trong Liệt kê 2) chứa từng tên (bây giờ tôi sẽ giữ nó thật đơn giản) và SSN (Số an sinh xã hội) của mỗi người chạy thi cùng với cá thể Race mà cô ta hay anh ta đang tham gia. Liệt kê 2. Một Runner đơn giản liên quan đến một Race public class Runner { private String name; private String ssn; private Race race; //setters and getters left out... } Như bạn có thể thấy trong các Liệt kê 1 và 2, tôi đã mô hình hóa một cách hợp lý một mối quan hệ nhiều-một giữa những vận động viên (runners) và một cuộc đua (race). Trong một tình huống thực tế, có lẽ sẽ thích hợp hơn là tạo ra liên kết nhiều- nhiều (những vận động viên thường tham gia nhiều cuộc đua), nhưng tôi sẽ giữ nó dễ dàng. Bây giờ tôi cũng đã bỏ qua các hàm tạo, các trình đặt (setter) và các trình nhận (getter). Tôi sẽ trình bày chúng sau. Các chú thích trong JPA Lấy hai đối tượng đã sẵn sàng như vậy cho SimpleJPA không phải là vấn đề quá khó. Trước tiên, tôi phải biểu thị ý định của mình để làm cho chúng có khả năng tồn tại lâu bền bằng cách thêm chú thích @Entity cho từng đối tượng. Tôi cũng cần mô tả đúng các mối quan hệ bằng cách sử dụng chú thích @OneToMany trong đối tượng Race và chú thích @ManyToOne trong đối tượng Runner. Người ta gắn chú thích @Entity vào vào mức lớp và các chú thích mối quan hệ được gắn vào mức getter. Tất cả điều này được thể hiện trong các Liệt kê 3 và 4: Liệt kê 3. Một Race có chú thích-JPA @Entity public class Race { private String name; private String location; private double distance; private List runners; @OneToMany(mappedBy = "race") public List getRunners() { return runners; } //other setters and getters left out... } Trong Liệt kê 3, tôi đã gắn phương thức getRunners với một chú thích @OneToMany. Tôi cũng đã quy định rằng có thể tìm thấy mối quan hệ này bằng cách sử dụng đặc tính race trên thực thể Runner. Trong Liệt kê 4, theo cách tương tự tôi sẽ chú thích phương thức getRace trong đối tượng Runner. Liệt kê 4. Một Runner có chú thích-JPA @Entity public class Runner { private String name; private String ssn; private Race race; @ManyToOne public Race getRace() { return race; } //other setters and getters left out... } Hầu hết các kho dữ liệu (quan hệ hay không quan hệ) cần một số cách mô tả tính duy nhất giữa các dữ liệu. Vì vậy, nếu tôi muốn làm cho hai đối tượng này tồn tại lâu dài trong một kho dữ liệu, ít nhất tôi phải thêm các ID (mã định danh) cho chúng. Trong Liệt kê 5, tôi đã thêm một thuộc tính id của kiểu BigInteger vào đối tượng miền Race. Tôi cũng sẽ làm điều tương tự với Runner. Liệt kê 5. Thêm một ID cho Race @Entity public class Race { private String name; private String location; private double distance; private List runners; private BigInteger id; @Id public BigInteger getId() { return id; } @OneToMany(mappedBy = "race") public List getRunners() { return runners; } //other setters and getters left out... } Chú thích @Id trong Liệt kê 5 không cung cấp bất kỳ thông tin nào về cách quản lý ID. Chương trình sẽ cho rằng tôi đang làm điều đó thủ công, chứ không phải bằng cách sử dụng một EntityManager chẳng hạn. Xem xét SimpleJPA Cho đến nay, tôi đã không thực hiện bất cứ điều gì cụ thể với SimpleDB. Nói chung, có thể chú thích các đối tượng Race và Runner bằng các chú thích JPA và có thể duy trì chúng trong bất kỳ kho dữ liệu nào được một công cụ JPA hỗ trợ. Các tùy chọn gồm có Oracle, DB2, MySQL và (như bây giờ bạn đã có thể đoán được) SimpleDB. SimpleJPA là một công cụ nguồn mở của JPA cho SimpleDB của Amazon. Trong khi nó không hỗ trợ toàn bộ đặc tả của JPA (ví dụ, bạn không thể kết nối vào các truy vấn JPA), nó hỗ trợ một tập hợp con đủ lớn đáng để khai thác. Một ưu điểm lớn về sử dụng SimpleJPA là nó cố gắng xử lý liên tục các vấn đề từ điển mà tôi đã thảo luận trong phần đầu của bài này. SimpleJPA thực hiện chuyển đổi chuỗi và bất kỳ việc đệm thêm tiếp theo nào (nếu cần) cho các đối tượng dựa trên các kiểu số. Chủ yếu, điều này có nghĩa là bạn không cần phải thay đổi mô hình miền của mình để phản ánh các kiểu String. (Có một ngoại lệ cho quy tắc đó, tôi sẽ giải thích sau đây). Vì SimpleJPA là một công cụ JPA, nên bạn có thể dễ dàng sử dụng các đối tượng miền tuân thủ-JPA với nó. SimpleJPA chỉ yêu cầu bạn sử dụng các ID của String, có nghĩa là thuộc tính id của bạn phải là một java.lang.String. Để làm cho mọi việc trở nên dễ dàng hơn, SimpleJPA cung cấp lớp IdedTimestampedBase, để quản lý đặc tính ID của đối tượng miền, cũng như các thuộc tính ngày created (đã tạo) và updated (đã cập nhật). (Trong lúc triển khai bên dưới một sản phẩm, SimpleDB tạo ra một Id duy nhất). Gửi ứng dụng tới SimpleJPA Để làm cho các lớp Race và Runner tuân theo SimpleJPA, tôi có thể hoặc mở rộng lớp cơ sở tiện dụng của SimpleJPA hoặc thay đổi từng thuộc tính id của lớp từ BigInteger sang String. Tôi đã chọn tùy chọn đầu tiên, như trong Liệ kê 6: Liệt kê 6. Thay đổi Race để sử dụng lớp cơ sở IdedTimestampedBase của SimpleJPA @Entity public class Race extends IdedTimestampedBase{ private String name; private String location; private double distance; private List runners; @OneToMany(mappedBy = "race") public List getRunners() { return runners; } //other setters and getters left out... } Tôi sẽ không chỉ cho bạn thấy một mã tương tự cho Runner, nhưng cứ tự nhiên thực hiện tạo mã một mình: chỉ cần mở rộng IdedTimestampedBase và loại bỏ đặc tính id khỏi Runner. Việc cập nhật các ID cho Race và Runner là bước đầu tiên làm cho ứng dụng chạy đua tuân theo SimpleJPA. Tiếp theo, tôi cần đổi các kiểu dữ liệu nguyên thủy (như double, int và float) để lấy các đối tượng như Integer (Số nguyên) và BigDecimal (Số thập phân lớn). Tôi sẽ bắt đầu với thuộc tính distance (khoảng cách) của Race. Tôi đã thấy BigDecimal tin cậy hơn Double (trong bản phát hành hiện tại của SimpleJPA), do đó, tôi đã thay đổi đặc tính distance của Race thành BigDecimal, như trong Liệt kê 7: Liệt kê 7. Thay đổi khoảng cách sang BigDecimal @Entity public class Race extends IdedTimestampedBase{ private String name; private String location; private BigDecimal distance; private List runners; @OneToMany(mappedBy = "race") public List getRunners() { return runners; } //other setters and getters left out... } Bây giờ cả hai Runner và Race đã sẵn sàng được duy trì thông qua một công cụ của SimpleJPA. Sử dụng SimpleJPA với SimpleDB Việc thao tác với các đối tượng miền của bạn dựa vào SimpleDB với SimpleJPA không có bất kỳ khác biệt nào so với việc thực hiện dựa vào một cơ sở dữ liệu quan hệ bình thường với một công cụ JPA. Nếu bạn đã từng thực hiện bất kỳ việc phát triển ứng dụng nào với JPA, thì bạn không có gì ngạc nhiên về nó cả. Điều duy nhất, có thể là mới, là việc cấu hình EntityManagerFactoryImpl của SimpleJPA, để yêu cầu các thông tin về Các dịch vụ web của Amazon (Amazon Web Services) của bạn và tên tiền tố cho tên miền SimpleDB của bạn. (Một tùy chọn khác sẽ cung cấp một tệp các thuộc tính có chứa các thông tin của bạn trên đường dẫn lớp (classpath)). Khi tạo một cá thể EntityManagerFactoryImpl của SimpleJPA, việc sử dụng tên tiền tố của bạn sẽ dẫn đến các miền SimpleDB bắt đầu với tiền tố của bạn tiếp theo là một dấu gạch ngang rồi đến tên đối tượng miền của bạn. Vì vậy, nếu tôi quy định "B50" làm tiền tố của mình, thì khi tôi tạo một mục Race trong SimpleDB, tên miền sẽ là "b50-Race". Một khi bạn đã tạo ra một cá thể EntityManagerFactoryImpl của SimpleDB, giao diện này sẽ hướng dẫn mọi thứ khác. Bạn sẽ cần một cá thể EntityManager đã thu được từ EntityManagerFactoryImpl, như trong Liệt kê 8: Liệt kê 8. Thu được một cá thể EntityManager Map props = new HashMap(); props.put("accessKey","..."); props.put("secretKey",".."); EntityManagerFactoryImpl factory = new EntityManagerFactoryImpl("b50", props); EntityManager em = factory.createEntityManager(); Thao tác các đối tượng miền Một khi bạn gọi một cá thể EntityManager, bạn có thể thao tác các đối tượng miền tùy ý. Ví dụ, tôi có thể tạo ra một cá thể Race như sau: Liệt kê 9. Tạo một cá thể Race Race race = new Race(); race.setName("Charlottesville Marathon"); race.setLocation("Charlottesville, VA"); race.setDistance(new BigDecimal(26.2)); em.persist(race); Trong Liệt kê 9, SimpleJPA xử lý tất cả các yêu cầu HTTP để tạo ra Race trong đám mây. Việc sử dụng SimpleJPA có nghĩa là tôi cũng có thể lấy ra cuộc chạy thi (race) bằng cách sử dụng một truy vấn JPA, như trong Liệt kê 10. (Hãy nhớ rằng bạn không thể kết nối với các truy vấn này, nhưng tôi vẫn có thể tìm kiếm chúng bằng các con số). Liệt kê 10. Tìm một cuộc thi chạy theo khoảng cách Query query = em.createQuery("select o from Race o where o.distance = :dist"); query.setParameter("dist", new BigDecimal(26.2)); List races = query.getResultList(); for(Race race : races){ System.out.println(race); } Từ các số thành các chuỗi ký tự Phép thuật số thành các chuỗi ký tự trong việc triển khai bên dưới của SimpleJPA đặc biệt tốt đẹp; ví dụ, nếu bạn cho phép in truy vấn trong SimpleJPA, bạn có thể thấy nó ban hành các truy vấn nào cho SimpleDB. Truy vấn đã gửi đi được hiển thị trong Liệt kê 11. Lưu ý cách mã hóa distance. Liệt kê 11. SimpleJPA xử lý tốt các số! amazonQuery: Domain=b50-Race, query=select * from `b50-Race` where `distance` = '0922337203685477583419999999999999928946' Việc đệm thêm và mã hóa tự động làm cho mọi thứ trở nên dễ dàng hơn nhiều, bạn có nghĩ vậy không? Các mối quan hệ trong SimpleJPA Mặc dù SimpleDB không cho phép các kết nối miền trong các truy vấn, bạn vẫn có thể có các mục liên quan trên các miền. Cũng giống như tôi đã giới thiệu cho bạn trong Phần 1, bạn có thể chỉ cần lưu trữ khóa của một đối tượng có liên quan trong đối tượng khác và sau đó lấy ra đối tượng đó khi bạn cần nó. Đó cũng là những gì mà SimpleJPA làm. Ví dụ, ở trên tôi đã chỉ cho bạn thấy cách liên kết các Runner tới một Race bằng cách sử dụng các chú thích JPA. Vì vậy, tôi có thể tạo ra một cá thể của một Runner, thêm cá thể race hiện có vào nó, rồi duy trì cá thể Runner, như trong Liệt kê 12: Liệt kê 12. Các mối quan hệ với SimpleJPA Runner runner = new Runner(); runner.setName("Mark Smith"); runner.setSsn("555-55-5555"); runner.setRace(race); race.addRunner(runner); em.persist(runner); em.persist(race); //update the race now that it has a runner Cũng lưu ý trong Liệt kê 12 rằng tôi phải cập nhật cá thể Race để phản ánh thực tế là tôi đã thêm một cá thể Runner vào nó (cũng lưu ý, tôi đã thêm một phương thức addRunner vào Race để thêm một Runner vào Collection nội bộ của các Runner). Một lần nữa, nếu tôi tìm kiếm một cuộc chạy thi theo khoảng cách của nó, tôi cũng có thể nhận được một danh sách các runner (những vận động viên) trong cuộc chạy đua đó giống như Liệt kê 13: Liệt kê 13. Thêm mối quan hệ Query query = em.createQuery("select o from Race o where o.distance = :dist"); query.setParameter("dist", new BigDecimal(26.2)); List races = query.getResultList(); for(Race races : race){ System.out.println(race); List runners = race.getRunners(); for(Runner rnr : runners){ System.out.println(rnr); } } Việc sử dụng một cá thể EntityManager cho phép tôi xóa các thực thể qua phương thức remove, như trong Liệt kê 14: Liệt kê 14. Loại bỏ một cá thể lớp Query query = em.createQuery("select o from Race o where o.distance = :dist"); query.setParameter("dist", new BigDecimal(26.2)); List races = query.getResultList(); for(Race races : race){ em.remove(race); } Trong khi tôi đã loại bỏ một cá thể Race trong Liệt kê 14, thì vẫn không loại bỏ được bất kỳ các Runners nào có liên quan với nó. (Tất nhiên, tôi có thể xử lý điều này bằng cách sử dụng chú thích EntityListeners của JPA, có nghĩa là tôi có thể móc nối vào một sự kiện loại bỏ và sử dụng nó để loại bỏ các cá thể Runner). Kết luận Bài viết về SimpleDB đã cho bạn thấy cách thao tác các đối tượng trong kho dữ liệu không quan hệ bằng cách sử dụng API của các dịch vụ web của Amazon (Amazon Web services) và SimpleJPA. SimpleJPA thực hiện một tập hợp con của API tồn tại lâu bền của Java (JPA - Java Persistence API) để làm cho đối tượng tồn tại lâu bền trong SimpleDB dễ dàng hơn. Một trong những tiện lợi về sử dụng SimpleJPA, như bạn đã thấy, là ở chỗ nó tự động chuyển đổi các kiểu nguyên thủy thành các đối tượng chuỗi ký tự mà SimpleDB nhận ra. SimpleJPA cũng xử lý tự động các quy tắc không kết nối của SimpleDB cho bạn, làm cho việc mô hình hóa các mối quan hệ trở nên dễ dàng hơn. Các giao diện trình nghe rộng rãi của SimpleJPA cũng giúp bạn có khả năng thực hiện các quy tắc toàn vẹn dữ liệu logic trước đây chỉ có ở thế giới quan hệ. Điểm mấu chốt về SimpleJPA là nó có thể giúp bạn truy cập khả năng mở rộng quy mô có ý nghĩa, không tốn kém một cách nhanh chóng và dễ dàng. Với SimpleJPA, bạn có thể sử dụng kiến thức mà bạn đã tích lũy từ nhiều năm làm việc với các khung công tác như Hibernate trong một môi trường lưu trữ dựa trên đám mây, không quan hệ.