Xem mẫu
- Máy ứng dụng của Google cho Java: Phần 3: Lưu giữ lâu bền và các mối
quan hệ
Lưu giữ lâu bền dựa vào Java và kho lưu trữ dữ liệu của Máy ứng dụng của
Google
Rick Hightower , Giám đốc, eBlox
Tóm tắt: Lưu giữ lâu bền dữ liệu là một phần quan trọng của việc phân phối ứng
dụng có khả năng mở rộng trong các môi trường doanh nghiệp. Trong bài viết cuối
cùng của loạt bài giới thiệu của mình về Máy ứng dụng của Google (Google App
Engine) cho Java™, Rick Hightower chấp nhận các thách thức của các khung
công tác lưu giữ lâu bền dựa trên Java hiện có của Máy ứng dụn g (App Engine).
Tìm hiểu các chi tiết về tại sao việc lưu giữ lâu bền bằng Java trong bản các phát
hành trước đây chưa phải là hoàn toàn sẵn sàng cho thời kỳ cao điểm, đồng thời
cũng nhận được một trình diễn làm việc thực sự về những gì mà bạn có thể làm để
lưu giữ lâu bền dữ liệu trong các ứng dụng của Máy ứng dụng cho Java. Chú ý
rằng bạ
Máy ứng dụng cho Java cố gắng loại bỏ mối lo phải viết một tầng lưu giữ lâu bền
cho các ứng dụng Web mở rộng được, nhưng nó thực hiện mục đích đó đến mức
nào? Trong bài viết này, tôi kết thúc bài giới thiệu của mình về Máy ứng dụng cho
Java với một tổng quan về khung công tác lưu giữ lâu bền của nó, dựa trên các đối
tượng dữ liệu Java (Java Data Objects-JDO) và API lưu giữ lâu bền của Java (Java
Persistence API-JPA). Mặc dù ban đầu tỏ ra nhiều hứa hẹn, việc lưu giữ lâu bền
dựa trên Java của Máy ứng dụng hiện tại có một số nhược điểm nghiêm trọng mà
tôi sẽ giải thích và chứng tỏ các nhược điểm đó. Bạn sẽ tìm hiểu xem việc lưu giữ
lâu bền của Máy ứng dụng cho Java hoạt động như thế nào, những thách thức là gì
và bạn có những tùy chọn lưu giữ lâu bền nào khi làm việc với nền tảng đám mây
của Google dành cho các nhà phát triển Java.
Khi bạn đọc bài viết này và làm việc qua các ví dụ, bạn sẽ muốn ghi nhớ một thực
tế là Máy ứng dụng cho Java hiện nay là một bản phát hành xem trước. Khi mà
việc lưu giữ lâu bền dựa trên Java có thể không phải là tất cả những gì mà bạn có
thể hy vọng, hoặc cần có, vào lúc này, điều này là có thể và cần thay đổi trong
tương lai. Những gì mà tôi học được trong khi viết bài hướng dẫn này là việc sử
dụng Máy ứng dụng cho Java để phát triển ứng dụng Java tập trung vào dữ liệu, có
khả năng mở rộng, hiện nay, không phải dành cho những người nhút nhát hay bảo
thủ. Nó giống như lặn xuống đáy bể bơi: Không còn ở trong tầm mắt của người
cứu hộ nữa và dự án của bạn chìm xuồng hoặc bơi được là tùy thuộc nơi bạn.
- Chú ý rằng ứng dụng ví dụ trong bài viết này được dựa trên ứng dụng quản lý liên
hệ đã phát triển trong Phần 2 của bài viết này. Bạn sẽ cần phải có ứng dụng đó đã
xây dựng và chạy được để tiếp tục các ví dụ ở đây.
Thành phần cơ bản và các sự trừu tượng hóa có lỗ rò
Cũng giống như Máy ứng dụng của Google nguyên thủy, Máy ứng dụng cho Java
dựa vào cơ sở hạ tầng nội bộ của Google với Ba Trụ Lớn (Big Three) để phát triển
ứng dụng có khả năng mở rộng: phân tán, tạo bản sao và cân bằng tải. Vì bạn đang
làm việc với cơ sở hạ tầng của Google, nên hầu hết các phép thuật này xảy ra ở
hậu trường và bạn có thể truy cập qua các API tiêu chuẩn của Máy ứng dụng cho
Java. Giao diện kho lưu trữ dữ liệu (datastore) được dựa trên JDO và JPA, mà
chính chúng lại dựa trên dự án nguồn mở DataNucleus. Máy ứng dụng cho Java
cũng cung cấp một API tiếp hợp (adapter) mức thấp để làm việc trực tiếp với kho
lưu trữ dữ liệu của Máy ứng dụng cho Java, kho lưu trữ dữ liệu này dựa trên việc
thực hiện BigTable của Google (xem Phần 1 để biết thêm về BigTable).
Tuy nhiên, việc lưu giữ lâu bền dữ liệu của Máy ứng dụng cho Java là không hoàn
toàn đơn giản như việc lưu giữ lâu bền trong Máy ứng dụng của Google thuần.
Các giao diện JDO và JPA lộ ra một số sự trừu tượng hóa có lỗ rò do thực tế là
BigTable không phải là một cơ sở dữ liệu quan hệ. Ví dụ, trong Máy ứng dụng cho
Java, bạn không thể thực hiện các truy vấn mà chúng thực hiện các phép nối
(join). Bạn có thể thiết lập các quan hệ trong JPA và JDO, nhưng chúng chỉ có thể
được sử dụng để lưu giữ lâu bền các quan hệ. Và khi bạn lưu giữ lâu bền các đối
tượng, chúng chỉ có thể được duy trì trong cùng một giao dịch nguyên tử nếu
chúng ở trong cùng một nhóm thực thể. Theo quy ước, các quan hệ là quan hệ sở
hữu nằm trong cùng nhóm thực thể với thực thể cha mẹ. Ngược lại, các mối quan
hệ không sở hữu nằm trong các nhóm thực thể tách biệt.
Nghĩ lại về chuẩn hóa dữ liệu
Làm việc với kho lưu trữ dữ liệu có khả năng mở rộng của Máy ứng dụng đòi hỏi
bạn phải suy nghĩ lại luận thuyết của mình về các lợi ích của dữ liệu được chuẩn
hóa. Tất nhiên, nếu bạn đã làm việc đủ lâu trong thế giới thực, có thể bạn đã phải
một hai lần hy sinh sự chuẩn hóa để đổi lấy hiệu năng. Sự khác biệt là, khi làm
việc với kho lưu trữ dữ liệu của Máy ứng dụng, bạn phải không chuẩn hóa sớm
hơn và thường xuyên. Chưa chuẩn hóa không còn là một từ xấu nữa; thay vào đó,
nó là một công cụ thiết kế mà bạn sẽ áp dụng trong nhiều khía cạnh của các ứng
dụng của Máy ứng dụng cho Java của bạn.
Nhược điểm chính với Máy ứng dụng cho sự lưu giữ lâu bền có lỗ rò của Java sẽ
xuất hiện khi bạn cố gắng chuyển một ứng dụng được viết cho một RDBMS tới
Máy ứng dụng cho Java. Kho lưu trữ dữ liệu của Máy ứng dụng cho Java không
- phải là một sự thay thế ngay lập tức cho một c ơ sở dữ liệu quan hệ, do đó, những
gì mà bạn làm với Máy ứng dụng cho Java có thể không dễ dàng diễn dịch để
chuyển sang cho RDBMS. Lấy một lược đồ hiện có và mang nó sang kho lưu trữ
dữ liệu là một kịch bản thậm chí ít khả năng hơn. Nếu bạn quyết định chuyển một
ứng dụng doanh nghiệp Java kế thừa tới Máy ứng dụng, hãy nên tiến hành thận
trọng và sao lưu nó với bản phân tích. Máy ứng dụng của Google là một nền tảng
dành cho các ứng dụng được thiết kế riêng biệt cho nó. Sự hỗ trợ của Máy ứng
dụng của Google cho Java cho JDO và JPA cho phép các ứng dụng này được
chuyển ngược trở lại thành các ứng dụng doanh nghiệp truyền thống hơn, dù là
chưa chuẩn hóa.
Những rắc rối với các mối quan hệ
Một nhược điểm khác của Máy ứng dụng cho Java trong bản phát hành xem trước
hiện nay là việc xử lý các quan hệ của nó. Để tạo các quan hệ, hiện tại bạn phải sử
dụng các phần mở rộng cho JDO đặc thù riêng cho Máy ứng dụng cho Java. Biết
rằng các khóa được tạo ra dựa trên các tạo phẩm của BigTable — đó là, "khóa
chính " có khóa đối tượng-cha mẹ được mã hóa vào tất cả các khóa của con của nó
— bạn sẽ phải quản lý dữ liệu của mình trong một cơ sở dữ liệu không quan hệ.
Một hạn chế khác là việc lưu giữ lâu bền dữ liệu. Các sự phức tạp nảy sinh nếu
bạn sử dụng lớp Key (Khóa) không tiêu chuẩn của Máy ứng dụng cho Java. Trước
hết, bạn sử dụng Key không tiêu chuẩn như thế nào khi chuyển mô hình của bạn
sang một RDBMS? Thứ hai, máy GWT không thể dịch lớp Key, vì thế bất kỳ đối
tượng mô hình nào sử dụng lớp này không thể được sử dụng như một phần của
ứng dụng GWT của bạn.
Tất nhiên, tại thời điểm viết bài này, Máy ứng dụng của Google cho Java mới chỉ
là ở chế độ xem trước. Nó không được xem là đã sẵn sàng cho thời kỳ cao điểm.
Điều này trở nên càng rõ ràng hơn khi nghiên cứu tài liệu hướng dẫn về các quan
hệ trong JDO, rất thưa thớt và chứa các ví dụ chưa hoàn chỉnh.
Bộ công cụ phát triển của Máy ứng dụng cho Java được gửi kèm một loạt các
chương trình ví dụ. Nhiều ví dụ trong đó sử dụng JDO, mà không sử dụng JPA.
Không có một ví dụ nào (kể cả ví dụ có tên là jdoexamples) nêu thí dụ về một
quan hệ dù là đơn giản. Thay vào đó, tất cả các ví dụ chỉ sử dụng một đối tượng để
lưu trữ dữ liệu trong kho lưu trữ dữ liệu. Nhóm thảo luận về Máy ứng dụng của
Google cho Java bị tràn ngập những câu hỏi về cách làm thế nào để một quan hệ
đơn giản hoạt động được, với vài câu trả lời. Một số nhà phát triển hình như đã có
khả năng làm cho nó hoạt động, nhưng rất vất vả và gặp một số khó khăn.
Điểm mấu chốt về các quan hệ trong Máy ứng dụng cho Java là bạn cần phải quản
lý chúng mà không được hỗ trợ nhiều từ JDO hay JPA. Tuy nhiên, BigTable của
Google là một công nghệ đã được thử thách để sản xuất các ứng dụng có khả năng
- mở rộng và bạn có thể xây dựng bên trên nó. Việc xây dựng bên trên BigTable
giải phóng bạn, không cần phải làm việc với mặt ngoài API, còn chưa được hậu
thuẫn đầy đủ. Mặt khác, bạn sẽ phải làm việc với một API mức thấp hơn.
JDO trong Máy ứng dụng cho Java
Khi mà việc chuyển một ứng dụng Java truyền thống sang Máy ứng dụng cho Java
có thể không có ý nghĩa và thậm chí đã biết rằng có những thách thức về các quan
hệ, vẫn có các kịch bản lưu giữ lâu bền mà ở đây việc sử dụng nền tảng này là có
ý. Tôi sẽ kết thúc bài này bằng một ví dụ làm việc được, mang lại cho bạn một
hương vị về việc Máy ứng dụng cho Java thực hiện lưu giữ lâu bền như thế nào.
Chúng ta sẽ bắt đầu với ứng dụng quản lý-liên hệ được xây dựng trong Phần 2, lần
này đi qua các thủ tục để thêm hỗ trợ cho việc lưu giữ lâu bền các đối tượng
Contact bằng các phương tiện của kho lưu trữ dữ liệu của Máy ứng dụng cho Java.
Trong bài trước, bạn đã tạo một GWT GUI đơn giản thực hiện các hoạt động
CRUD trên các đối tượng Contact. Bạn đã định nghĩa giao diện đơn giản như thấy
trong Liệt kê 1:
Liệt kê 1. Giao diện ContactDAO đơn giản
package gaej.example.contact.server;
import java.util.List;
import gaej.example.contact.client.Contact;
public interface ContactDAO {
void addContact(Contact contact);
void removeContact(Contact contact);
- void updateContact(Contact contact);
List listContacts();
}
Tiếp theo, bạn đã tạo ra một phiên bản giả, làm việc với dữ liệu trong một bộ sưu
tập nằm ở bộ nhớ, như thấy trong Liệt kê 2:
Liệt kê 2. ContactDAOMock làm giả DAO
package gaej.example.contact.server;
import gaej.example.contact.client.Contact;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public class ContactDAOMock implements ContactDAO {
- Map map = new LinkedHashMap();
{
map.put("rhightower@mammatus.com", new Contact("Rick
Hightower",
"rhightower@mammatus.com", "520-555-1212"));
map.put("scott@mammatus.com", new Contact("Scott
Fauerbach",
"scott@mammatus.com", "520-555-1213"));
map.put("bob@mammatus.com", new Contact("Bob Dean",
"bob@mammatus.com", "520-555-1214"));
}
public void addContact(Contact contact) {
String email = contact.getEmail();
map.put(email, contact);
}
public List listContacts() {
return Collections.unmodifiableList(new
ArrayList(map.values()));
}
- public void removeContact(Contact contact) {
map.remove(contact.getEmail());
}
public void updateContact(Contact contact) {
map.put(contact.getEmail(), contact);
}
}
Bây giờ hãy xem điều gì sẽ xảy ra khi bạn thay thế việc thực hiện mô hình giả
bằng một phiên bản của ứng dụng tương tác với kho lưu trữ dữ liệu của Máy ứng
dụng của Google. Với ví dụ này, bạn sẽ sử dụng JDO để lưu giữ lâu bền lớp
Contact. Một ứng dụng được viết bằng cách sử dụng các trình cắm thêm Eclipse
của Google (Google Eclipse Plugin) đã có tất cả các thư viện mà nó cần để sử
dụng JDO. Nó cũng bao gồm một tệp jdoconfig.xml, do đó một khi bạn chú giải
lớp Contact, bạn sẽ sẵn sàng để bắt đầu sử dụng JDO.
Liệt kê 3 cho thấy giao diện ContactDAO được mở rộng để sử dụng API JDO để
lưu giữ lâu bền, truy vấn, cập nhật và xóa các đối tượng:
Liệt kê 3. ContactDAO với JDO
package gaej.example.contact.server;
- import gaej.example.contact.client.Contact;
import java.util.List;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
public class ContactJdoDAO implements ContactDAO {
private static final PersistenceManagerFactory pmfInstance = JDOHelper
.getPersistenceManagerFactory("transactions-
optional");
public static PersistenceManagerFactory getPersistenceManagerFactory()
{
return pmfInstance;
}
public void addContact(Contact contact) {
PersistenceManager pm = getPersistenceManagerFactory()
.getPersistenceManager();
try {
- pm.makePersistent(contact);
} finally {
pm.close();
}
}
@SuppressWarnings("unchecked")
public List listContacts() {
PersistenceManager pm = getPersistenceManagerFactory()
.getPersistenceManager();
String query = "select from " + Contact.class.getName();
return (List) pm.newQuery(query).execute();
}
public void removeContact(Contact contact) {
PersistenceManager pm = getPersistenceManagerFactory()
.getPersistenceManager();
try {
pm.currentTransaction().begin();
// We don't have a reference to the selected Product.
// So we have to look it up first,
- contact = pm.getObjectById(Contact.class,
contact.getId());
pm.deletePersistent(contact);
pm.currentTransaction().commit();
} catch (Exception ex) {
pm.currentTransaction().rollback();
throw new RuntimeException(ex);
} finally {
pm.close();
}
}
public void updateContact(Contact contact) {
PersistenceManager pm = getPersistenceManagerFactory()
.getPersistenceManager();
String name = contact.getName();
String phone = contact.getPhone();
String email = contact.getEmail();
try {
pm.currentTransaction().begin();
- // We don't have a reference to the selected Product.
// So we have to look it up first,
contact = pm.getObjectById(Contact.class,
contact.getId());
contact.setName(name);
contact.setPhone(phone);
contact.setEmail(email);
pm.makePersistent(contact);
pm.currentTransaction().commit();
} catch (Exception ex) {
pm.currentTransaction().rollback();
throw new RuntimeException(ex);
} finally {
pm.close();
}
}
}
Theo từng phương thức
Bây giờ chúng ta hãy xem xét những gì đang xảy ra với mỗi một trong các phương
thức trong Liệt kê 3. Bạn sẽ thấy rằng trong khi các tên phương thức có thể là mới,
hành động của chúng phần nhiều là đã quen thuộc.
- Trước tiên, để truy cập vào PersistenceManager, bạn đã tạo ra một
PersistenceManagerFactory tĩnh. Nếu bạn đã làm việc với JPA từ trước,
PersistenceManager tương tự với một EntityManager trong JPA. Nếu bạn đã làm
việc với Hibernate, PersistenceManager tương tự như một phiên Hibernate. Về
bản chất, PersistenceManager là giao diện chính cho hệ thống lưu giữ lâu bền
JDO. Nó đại diện cho một phiên làm việc với cơ sở dữ liệu. Phương thức
getPersistenceManagerFactory() trả về PersistenceManagerFactory, được khởi tạo
tĩnh, như chỉ ra trong Liệt kê 4:
Liệt kê 4. getPersistenceManagerFactory() trả về PersistenceManagerFactory
private static final PersistenceManagerFactory pmfInstance = JDOHelper
.getPersistenceManagerFactory("transactions-optional");
public static PersistenceManagerFactory getPersistenceManagerFactory() {
return pmfInstance;
}
Phương thức addContact() bổ sung thêm một mối liên hệ mới vào kho lưu trữ dữ
liệu này. Để làm điều này, nó cần phải tạo ra một cá thể của PersistenceManager
và sau đó gọi phương thức makePersistence() của PersistenceManager này.
Phương thức makePersistence() lấy đối tượng Contact tạm thời (mà người dùng sẽ
phải điền vào trong GWT GUI) và làm cho nó thành một đối tượng lưu giữ lâu
bền. Tất cả những việc này được thể hiện trong Liệt kê 5:
Liệt kê 5. addContact()
- public void addContact(Contact contact) {
PersistenceManager pm = getPersistenceManagerFactory()
.getPersistenceManager();
try {
pm.makePersistent(contact);
} finally {
pm.close();
}
}
Chú ý trong Liệt kê 5 persistenceManager được gói trong một khối finally như thế
nào. Điều này đảm bảo nó xóa nguồn tài nguyên kết hợp với persistenceManager.
Phương thức listContact(), được hiển thị trong Liệt kê 6, tạo ra một đối tượng truy
vấn từ persistenceManager mà nó tìm kiếm. Nó gọi phương thức execute(), trả về
danh sách các Contact từ kho lưu trữ dữ liệu.
Liệt kê 6. listContact()
@SuppressWarnings("unchecked")
public List listContacts() {
PersistenceManager pm = getPersistenceManagerFactory()
- .getPersistenceManager();
String query = "select from " + Contact.class.getName();
return (List) pm.newQuery(query).execute();
}
Phương thức removeContact() tìm kiếm mối liên hệ bằng mã nhận dạng (ID) trước
khi nó loại bỏ mối liên hệ ấy khỏi kho lưu trữ dữ liệu, như chỉ ra trong Liệt kê 7.
Nó phải làm điều này chứ không phải chỉ xóa ngay mối liên hệ vì đối tượng
Contact đến từ GWT GUI không biết gì về JDO. Bạn phải lấy ra một Contact
được kết hợp với một vùng nhớ nhanh (cache) của PersistenceManager trước khi
bạn có thể xóa nó.
Liệt kê 7. removeContact()
public void removeContact(Contact contact) {
PersistenceManager pm = getPersistenceManagerFactory()
.getPersistenceManager();
try {
pm.currentTransaction().begin();
// We don't have a reference to the selected Product.
// So we have to look it up first,
contact = pm.getObjectById(Contact.class, contact.getId());
- pm.deletePersistent(contact);
pm.currentTransaction().commit();
} catch (Exception ex) {
pm.currentTransaction().rollback();
throw new RuntimeException(ex);
} finally {
pm.close();
}
}
Phương thức updateContact(), trong Liệt kê 8, tương tự như phương thức
removeContact(), ở chỗ là nó cũng tìm kiếm Contact. Phương thức
updateContact() sau đó sao chép các thuộc tính từ Contact. Các thuộc tính này
được chuyển như một đối số đến Contact, đã được tìm kiếm với trình quản lý lưu
giữ lâu bền. PersistenceManager kiểm tra các thay đổi của các đối tượng được tìm
kiếm. Nếu một đối tượng đã thay đổi, các thay đổi này được PersistenceManager
trút sang cơ sở dữ liệu khi một giao dịch cam kết.
Liệt kê 8. updateContact()
public void updateContact(Contact contact) {
PersistenceManager pm = getPersistenceManagerFactory()
.getPersistenceManager();
- String name = contact.getName();
String phone = contact.getPhone();
String email = contact.getEmail();
try {
pm.currentTransaction().begin();
// We don't have a reference to the selected Product.
// So we have to look it up first,
contact = pm.getObjectById(Contact.class, contact.getId());
contact.setName(name);
contact.setPhone(phone);
contact.setEmail(email);
pm.makePersistent(contact);
pm.currentTransaction().commit();
} catch (Exception ex) {
pm.currentTransaction().rollback();
throw new RuntimeException(ex);
} finally {
pm.close();
}
}
- Chú giải cho việc lưu giữ lâu bền đối tượng
Để một Contact được lưu giữ lâu bền, bạn phải nhận biết nó như là một đối tượng
có khả năng - lưu giữ lâu bền với chú giải @PersistenceCapable. Sau đó bạn cần
phải chú giải tất cả các trường có khả năng lưu giữ lâu bền của nó, như thể hiện
trong Liệt kê 9:
Liệt kê 9. Contact có khả năng lưu giữ lâu bền
package gaej.example.contact.client;
import java.io.Serializable;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Contact implements Serializable {
private static final long serialVersionUID = 1L;
@PrimaryKey
- @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private String name;
@Persistent
private String email;
@Persistent
private String phone;
public Contact() {
}
public Contact(String name, String email, String phone) {
super();
this.name = name;
this.email = email;
this.phone = phone;
}
public Long getId() {
return id;
- }
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
- public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
Nhờ sự kỳ diệu của lập trình hướng đối tượng và các nguyên lý thiết kế bằng giao
diện, bạn có thể thay thế ContactDAOMock ban đầu của bạn bằng
ContactJdoDAO mới. GWT GUI sau đó sẽ làm việc với JDO mà không cần thay
đổi.
Cuối cùng, cái thực sự thay đổi với sự trao đổi này là cách mà DAO được thuyết
minh trong dịch vụ, như thể hiện trong Liệt kê 10:
Liệt kê 10. RemoteServiceServlet
public class ContactServiceImpl extends RemoteServiceServlet implements
ContactService {
private static final long serialVersionUID = 1L;
//private ContactDAO contactDAO = new ContactDAOMock();
private ContactDAO contactDAO = new ContactJdoDAO();
nguon tai.lieu . vn