Võ Văn Hải's blog

Chỉ có một điều tuyệt đối đó là mọi thứ đều tương đối…

Chuyển mã JDBC Data Access Objects để sử dụng EJB3

Tính vững bền là một trong những thách thức lớn nhất trong xây dựng ứng dụng. Có nhiều lựa chọn để xây dựng các tầng vững bền. DAO (đối tượng truy cập dữ liệu) là một mẫu thiết kế phổ biến để xây dựng tầng vững bền trong ứng dụng J2EE.

Các nhà phát triển chủ yếu dùng mẫu thiết kế này để tách biệt mã JDBC khỏi logic nghiệp vụ. EJB3 Java Persistence API, định nghĩa API bền vững cho hệ nền Java dựa trên các giải pháp O-R (ánh xạ đối tượng-quan hệ) như Oracle TopLink và Jboss Hibernate, cho phép các nhà phát triển bỏ qua công đoạn xây dựng mã nguồn DAO và JDBC nhàm chán. Phần lớn các nhà phát triển sẽ sử dụng EJB3 Java Persistence API thay vì viết mã JDBC dùng DAO.

Một vài tháng trước tác giả đã chuyển từ thiết kế J2EE bằng trình ứng dụng Java Adventure Builder 1.0.1 module Web site khách hàng sang dùng EJB3 Java Persistence API. Trong bài viết này tác giả sẽ trình bày chi tiết những gì bạn cần để chuyển trình ứng dụng nền DAO sẵn có sang nền EJB3 Java Persistence API.

Tại sao chuyển?

Tại sao nên thực hiện chuyển đổi trong khi ứng dụng của bạn vẫn đang chạy tốt với các JDBC DAO? Sau đây là một vài lý do:

  • Bạn có thể giảm bớt các tạo tác, và sự phức tạp liên quan tới việc bảo trì mã nguồn DAO. Ví dụ, sau khi tác giả chuyển Adventure Builder Consumer Web site từ DAO sang EJB3 Java Persistence API, tác giả giảm được 16% mã nguồn và 36% số lớp (file) Java phải quản lý.

Bảng dưới đây so sánh số dòng mã nguồn trước và sau khi chuyển. Tác giả không tập trung vào việc thiết kế lại đối tượng hoặc kiểu dữ liệu mà tập trung vào việc chuyển mã nguồn đã có thành mã nguồn tương ứng cùng chức năng.

DAOs (Classes/Lines of Java code)

EJB3 (Classes/Lines of Java code)

EJB3 (Entities) Module

0/0

9 / 680

Web Module

64 / 3062

34 / 2097

DAO Factory

3 / 220

0/0

Total

67/3284

43/2777

  • Dùng mã nguồn API JDBC và SQL tự nhiên trong ứng dụng của bạn khiến ứng dụng đó kém linh động trong kết nối với cơ sở dữ liệu trên các hệ quản trị khác nhau. Ngược lại, các container của EJB3 như Oracle Application Server 10g dùng Oracle TopLink làm nguồn cung cấp bền vững đã được chứng nhận bởi nhiều cơ sở dữ liệu.
  • Các cơ chế bền vững đi kèm EJB3 như Oracle TopLink và JBoss Hibernate đem lại các lợi ích như: tăng bộ đệm, hiệu năng và khả năng mở rộng.

Tổng quan về EJB3 Java Persistence API

EJB 3.0 gần đây được nhắc tới nhiều. EJB 3.0 chuẩn hóa API bền vững cho nền Java. Các đối tượng bền vững, gọi là các thực thể, chính là các đối tượng viết hoàn toàn bằng POJO (Plain Old Java Objects), ký hiệu là @Entity. Một thực thể có thể nhúng một hoặc nhiều đối tượng persistence trong nó, các đối tượng này được gọi là đối tượng nhúng. EJB3 Java Persistence API hỗ trợ các khái niệm hướng đối tượng cơ bản như kế thừa và đa hình với các thực thể EJB3.

Bạn có thể định nghĩa quan hệ giữa các thực thể khác nhau. EJB3 Java Persistence API định nghĩa ánh xạ O – R dùng chú thích siêu dữ liệu của Java hoặc các bộ mô tả XML. Ví dụ một thực thể có truy xuất theo thuộc tính dùng chú thích siêu dữ liệu mô tả ánh xạ O – R :

@Entity

@Table(name = “SIGNON”)

public class User  implements java.io.Serializable {

protected String userId;

protected String password;

public User () {}

public User (String userId, String password) {

this.userId = userId;

this.password = password;

}

@Id

@Column(name=”USERNAME”)

public String getUserId() {

return userId;

}

public String getPassword() {

return password;

}

public void setUserId(String userid) {

this.userId = userid;

}

public void setPassword(String password) {

this.password = password;

}

}

EJB3 Java Persistence API giới thiệu EntityManager API (tạm dịch là API quản lý thực thể), được sử dụng cho các thao tác CRUD của các thực thể. API EntityManager được gọi đến trong ngữ cảnh giao dịch. Bạn có thể gọi nó bên ngoài một EJB container, ví dụ từ một ứng dụng Web mà không cần một session bean façade (giao diện session bean).

Truy vấn được tự động sinh ra hoặc được lưu như một truy vấn có tên trong các thực thể. Bạn có thể dùng EJBQL hoặc SQL tự nhiên để truy vấn thực thể. EJB 3.0 cũng giải quyết được nhiều hạn chế trước kia của EJBQL.

Vừa rồi là tổng quan về EJB3 Java Persistence API, tiếp theo sẽ là cách chuyển một ứng dụng dùng DAO sang dùng EJB3 Java Persistence API. Tác giả sẽ dùng module Website người dùng  của Java Adventure Builder làm ví dụ.

Chuyển ứng dụng sang dùng EJB3 Persistence

Nếu quyết định dùng EJB3 Java Persistence API trong ứng dụng dựa trên DAO, bạn có 2 lựa chọn: hoặc thay thế hoàn toàn các  DAO bằng EJB3 Java Persistence API, hoặc tiếp tục dùng các DAO và chỉ chuyển phần mã nguồn DAO đặc tả cơ sở dữ liệu bạn cần dùng sang EJB3 Java Persistence API. Trong bài này, tác giả sẽ thảo luận cả hai giải pháp:

Để hoàn thành việc chuyển đổi, bạn cần làm theo các bước sau:

  • Chuyển các đối tượng bền vững thành các thực thể
  • Chuyển các truy vấn sang dùng EJBQL
  • Cập nhật các ứng dụng của bạn để sử dụng EJB3 EntityManager API

Chuyển đối tượng Java sang thực thể EJB3

Đầu tiên, phải chuyển đối tượng giá trị sang thực thể EJB3, sau đó ánh xạ chúng đến  lược đồ dữ liệu của bạn. Quá trình thực hiện theo các bước sau:

  1. Xác định các đối tượng và quan hệ giữa chúng. Đây là bước quan trọng nhất trong chuyển ứng dụng, yêu cầu bạn phải hiểu mô hình ứng dụng, mô hình đối tượng và mô hình dữ liệu của mình. EJB3 Java Persistence API cho phép bạn ánh xạ một hoặc nhiều đối tượng Java trong cùng một bảng thành các thực thể hoặc các đối tượng nhúng. Bạn phải hiểu mô hình đối tượng và xác định quan hệ phù hợp giữa các đối tượng đó. Kiểm tra các đối tượng cẩn thận; có thể bạn quyết định mô hình hóa lại các đối tượng khi chuyển sang dùng thực thể EJB3.
  2. Tạo các thuộc tính bền vững và/hoặc các phương thức Setter and Getter còn thiếu cho các thực thể. EJB3 Java Persistence API cho phép bạn định nghĩa hai kiểu truy xuất: theo trường hoặc theo thuộc tính. Loại truy xuất được xác định tùy thuộc vào chú thích siêu dữ liệu trên các trường hoặc thuộc tính. Truy xuất trên trường dễ dùng hơn nhưng truy xuất trên thuộc tính cho phép che giấu dữ liệu. Nếu quyết định dùng loại truy xuất thuộc tính, bạn phải định nghĩa các thuộc tính vững bền (các phương thức setter/getter) cho thực thể và dùng ánh xạ chú thích siêu dữ liệu trên các thuộc tính.

EJB3 Java Persistence API cho phép bạn tạo ánh xạ siêu dữ liệu O – R dùng chú thích siêu dữ liệu hoặc mô tả XML. Trong các ví dụ của mình tác giả sử dụng chú thích siêu dữ liệu.

  1. Tạo các thực thể và ánh xạ siêu dữ liệu O-R. Bước cuối cùng là chuyển đối tượng bền vững sang thực thể. Một vài đối tượng sẽ trở thành các thực thể, số còn lại có thể thành các đối tượng nhúng. Một đối tượng nhúng là một đối tượng bền vững, không có nhận dạng riêng vì nó là một phần của thực thể khác. Ví dụ, đối tượng Java như Activity, AdventurePackage, Lodging, Transportation, Account và User trở thành các thực thể. Cả Account và ContactInformation được ánh xạ tới cùng một bảng dữ liệu nên ContactInformation trở thành một đối tượng nhúng.

Sau đây là ví dụ mã nguồn thực thể Account, sử dụng ContactInformation kiểu Embeddable. Trong ví dụ này, tác giả dùng chú thích siêu dữ liệu, dù có thể một số người thích dùng mô tả XML để định nghĩa ánh xạ O-R

@Entity

@Table(name = “ACT”)

public class Account  implements java.io.Serializable {

protected String userId;

protected ContactInformation info;

public Account () {}

public Account (String userId, ContactInformation info) {

this.userId = userId;

this.info = info;

}

@Id

@Column(name=”USERID”)

public String getUserId() {

return userId;

}

@Embedded

public ContactInformation getContactInformation() {

return info;

}

}

Đây là đoạn mã nguồn của một đối có khả năng nhúng được sử dụng bởi thực thể Account.

@Embeddable

@Table(name = “ACT”)

public class ContactInformation implements java.io.Serializable {

protected String telephone;

protected String email;

protected String familyName;

protected String givenName;

public ContactInformation() {}

public ContactInformation(String familyName,

String givenName,

String telephone,

String email){

this.givenName = givenName;

this.familyName = familyName;

this.email = email;

this.telephone = telephone;

}

@Column(name=”FIRSTNAME”)

public String getGivenName(){

return givenName;

}

}

Sau khi chuyển các đối tượng thành thực thể, bạn phải tạo một đơn vị bền vững, đóng gói các thực thể đó lại. Các thực thể có thể được đóng gói theo bất kỳ định dạng nào của J2EE như WAR, EJB-JAR hoặc một file jar thông thường, được đóng gói hoặc tham chiếu bởi một module J2EE.

Chuyển các truy vấn tới DAO sang truy vấn EJB3 có tên

EJB3 Java Persistence API quản lý thao tác CRUD cơ bản để đảm bảo tính bền vững, bạn không cần phải viết mã nguồn JDBC để tạo hoặc truy vấn các đối tượng. Tuy nhiên, bạn cần phải chuyển một số truy vấn trong DAO để có thể dùng một truy vấn có tên trên các thực thể. Khi bạn dùng EJB3 persistence, các truy vấn diến đạt bằng ngôn ngữ EJBQL.

Ví dụ, ứng dụng AdventureBuilder có một phương thức để tìm tất cả các chuyến bay sẵn sàng là PointbaseCatalogDAO.getTransportations, dùng một câu lệnh SQL như sau

SELECT transportationid, name, description, imageuri, price, origin,

destination,carrier,departuretime,

arrivaltime, class FROM Transportation

WHERE origin = ? AND destination = ? AND locale = ?

This must be migrated to use a named query on the entity, as follows:

@NamedQuery(

name=”getTransportations”,

queryString=”SELECT t FROM Transportation t WHERE t.origin =

:origin and t.destination=:destination and t.locale=:locale”

)

Cập nhật mã ứng dụng để sử dụng EntityManager API

Cuối cùng, bạn phải cập nhật ứng dụng để sử dụng EntityManager API. Bạn có 2 lựa chọn: hoặc cập nhật mã DAO để sử dụng EntityManager API thay thế cho mã JDBC, hoặc thay thế hoàn toàn mã DAO bằng Entity Manager API.

Các DAO được sử dụng để lưu bền vững các thay đổi trong cơ sở dữ liệu, hoặc để truy vấn cơ sở dữ liệu. Bạn sẽ cần thay đổi ứng dụng của mình để sử dụng EntityManager API phù hợp.

Các DAO là các đối tượng giao dịch, và EntityManager API được triệu gọi theo cách thức giao dịch, vì vậy, có thể bạn sẽ không phải thay đổi ngữ nghĩa giao dịch của ứng dụng.

Thay đổi DAO để sử dụng EntityManager API

DAO cho phép bạn lưu mã ứng dụng tách biệt khỏi logic vững bền. Nếu bạn sử dụng hướng tiếp cận này thì sẽ không làm ảnh hưởng tới mã ứng dụng sử dụng DAO. Bạn có thể sử dụng hoặc trình quản lý thực thể được quản lý mức bộ chứa (container-managed) hoặc mức ứng dụng (application-managed), và bạn có thể sử dụng hoặc JNDI lookup, hoặc dependency injection để lưu một thực thể của EntityManager. Thảo luận chi tiết về EntityManager và các đơn vị vững bền không thuộc phạm vi của bài báo này.

Dưới đây là các bước cần thực hiện để sử dụng EntityManager API từ DAO:

1. Lưu một thể hiện của EntityManager. Thay thế đoạn mã JDBC lấy về kết nối DataSource để lưu một thể hiện của EntityManager API. EntityManager chịu trách nhiệm thực thi các thao tác CRUD cho dữ liệu bền vững của bạn

Nếu bạn đang sử dụng một trình quản lý thực thể mức bộ chứa, bạn có thể lưu một thể hiện của trình quản lý thực thể bằng cách sử dụng dependency injection như sau:

@PersistentContext(unitName=”adventureBuilder”)

EntityManager em;

Dưới đây là đoạn mã ví dụ cho việc lưu một thể hiện của Application Managed EntityManager API:

@PersistenceUnit(unitName=”adventureBuilder”)

private EntityManagerFactory emf;

private EntityManager em = emf.createEntityManager();

Bạn nên đóng thực thể của trình quản lý thực thể mức ứng dụng bằng cách gọi phương thức em.close() sau khi hoàn thành.

Lưu ý rằng bạn chỉ có thể sử dụng dependency injection trong các lớp được quản lý, chẳng hạn như servlet, ejb, v.v… chứ không phải trong các lớp Java thông thường, và bạn phải sử dụng JNDI để tra cứu một thực thể của trình quản lý đối tượng như sau:

context = new InitialContext();

em = (EntityManager)context.lookup(JNDINames.AB_PM);

Lưu ý: Đoạn mã kiểm soát ngoại lệ đã được bỏ qua.

2. Lưu dữ liệu. Thay thế mã JDBC cho các thao tác insert, update, delete… để sử dụng EntityManager API.

Ví dụ, một trong số các đoạn mã DAO có câu lệnh insert như dưới đây để tạo một thực thể Account trong cơ sở dữ liệu trong phương thức createAccount:

private final static String INSERT_ACCOUNT_QUERY_STR =

“INSERT INTO ” +

DatabaseNames.ACCOUNT_TABLE

+ “(userid,email,firstname,lastname,”

+ “addr1,addr2,city,state,zip,country,”

+ “phone)” + “VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)”;

private void insertAccount(Account details) {

stmt = dbConnection.prepareStatement(INSERT_ACCOUNT_QUERY_STR);

stmt.setString(1, details.getUserId().trim() );

stmt.setString(2, info.getEMail().trim()  );

stmt.setString(3, info.getGivenName().trim() );

stmt.setString(4, info.getFamilyName().trim()  );

stmt.setString(5, info.getAddress().getStreetName1().trim()  );

}

Đoạn mã thay thế để sử dụng EntityManager API như sau:

private void insertAccount(Account details) {

em.persist(details);

}

Tương tự, bạn có thể sử dụng các phương thức remove, flush hay merge để chuyển mã JDBC cho các thao tác remove hoặc update. Bạn có thể sử dụng phương thức remove(Object entity) để xóa một thực thể khỏi cơ sở dữ liệu và sử dụng phương thức flush(Object entity) để các cập nhật được gửi tới cơ sở dữ liệu trước khi giao dịch hoàn thành, và sử dụng phương thức merge(Object entity) để cập nhật thể hiện thực thể có được trong một giao dịch khác.

3. Truy lục dữ liệu. Để truy lục dữ liệu khi sử dụng JDBC, bạn luôn viết câu lệnh SQL. Khi thay đổi mã để sử dụng EntityManager API, bạn có 2 cách lấy về dữ liệu:

    1. Tìm kiếm theo khóa chính. Bạn có thể sử dụng phương thức find(Class entityClass, Object primaryKey) của EntityManager để lấy về các thể hiện của thực thể. Ví dụ, nếu bạn có phương thức getAccount() trong DAO, đoạn mã chuyển đổi cho phương thức này sẽ như sau:

account = (Account) em.find(Account.class,userId);

    1. Tìm kiếm theo câu truy vấn bất kỳ. Bạn có thể sử dụng các truy vấn động, hoặc truy vấn có tên. Ví dụ, chúng ta đã định nghĩa một truy vấn có tên từ trước để lấy về Transportation phù hợp với điều kiện tìm kiếm cụ thể. Bạn có thể thay đổi mã để sử dụng một truy vấn động, hoặc truy vấn có tên thay cho các phương thức JDBC.

Query transQuery = em.createNamedQuery(“getTransportations”);

transQuery.setParameter(“origin”,origin);

transQuery.setParameter(“destination”,destination);

transQuery.setParameter(“locale”,locale.toString().trim());

List trans = transQuery.getResultList();

Thay thế DAO bằng EntityManager API

Các bước này khá giống với việc cập nhật DAO để sử dụng EntityManager API, nhưng có lẽ đòi hỏi nhiều thao tác hơn, và sẽ khiến mã lưu dữ liệu bền vững của bạn bị gắn chặt vào ứng dụng. Nhiều nhà phát triển sử dụng façade pattern để có thể sử dụng DAO mà không làm ảnh hưởng tới các ứng dụng sử dụng mã DAO của họ. Các façade này sử dụng DAO API để tạo mới hoặc truy vấn dữ liệu bền vững trong một ứng dụng. Nếu các ứng dụng của bạn sử dụng các façade DAO này thì sẽ rất dễ dàng di trú sang EJB3 Java Persistence API.

Dưới đây là các bước cần thực hiện để sử dụng EntityManager API thay thế cho DAO

  1. Lưu một thể hiện của EntityManager. Thay vì việc tạo ra một thể hiện của DAO, bạn phải lưu một thể hiện của EntityManager. Ví dụ, phương thức khởi tạo của AdventureBuilder CustomerFacade sử dụng đoạn mã sau để tạo ra một thể hiện của AccountDAO:

accountDao = (AccountDAO)

DAOFactory.getDAO(JNDINames.ACCOUNT_DAO_CLASS);

Nếu façade này là một lớp được quản lý thì bạn có thể sử dụng dependency injection như đề cập ở trên. Nếu nó là một lớp thông thường thì chúng ta có thể sử dụng JNDI và mã thay đổi sẽ như sau:

context = new InitialContext();

em = (EntityManager)context.lookup(JNDINames.AB_PM);

  1. Lưu dữ liệu. Bạn phải thay đổi mã để sử dụng EntityManager API để lưu dữ liệu bền vững thay vì sử dụng DAO.

Ví dụ, nếu bạn sử dụng DAO để tạo ra một account trong cơ sở dữ liệu, đoạn mã này sẽ như sau:

accountDao.create(accountDetails);

Bạn có thể thay đổi đoạn mã đó để sử dụng EntityManager API như sau:

em.persist(accountDetails);

  1. Truy lục dữ liệu. Như chúng ta đề cập từ trước, bạn luôn viết mã JDBC để truy lục dữ liệu khi sử dụng DAO và EntityManager API cung cấp 2 phương pháp để lấy về dữ liệu
    1. Tìm theo khóa chính. Bạn có thể sử dụng phương pháp Find của EntityManager để lấy về các thể hiện của thực thể. Bạn phải thay đổi mã để sử dụng EntityManagerAPI. Ví dụ, nếu bạn có phương thức getAccount() trong DAO, dưới đây sẽ là cách thức bạn lấy về account này bằng khóa chính của nó:

account = accountDao.getAccount(userId);

The migrated code will look like this:

Đoạn mã chuyển đổi sẽ như dưới đây:

account = (Account) em.find(Account.class,userId);

    1. Tìm theo câu truy vấn bất kỳ. Như chúng ta thảo luận từ trước, bạn có thể sử dụng hoặc một truy vấn động, hoặc truy vấn có tên. Bạn phải thay đổi mã để sử dụng các truy vấn có tên hoặc truy vấn động, thay vì việc sử dụng các phương thức DAO.

Sau khi cập nhật mã ứng dụng để loại bỏ các phương thức DAO và ứng dụng để sử dụng EntityManagerAPI thì DAO là ứng cử viên cần loại bỏ.

(sưu tầm)

One Response to “Chuyển mã JDBC Data Access Objects để sử dụng EJB3”

  1. Bach said

    nếu tìm thấy blog này sớm hơn thì chắc đã đạt điểm đc cao môn java rồi. Cám ơn anh

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: