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.
21 trang |
Chia sẻ: lylyngoc | Lượt xem: 1645 | Lượt tải: 1
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ệ.