1
Spring MVC
Mục lục
1 Hibernate
Trong nhiều năm, persistence (việc lưu trữ một đối tượng lên ổ đĩa hay một thiết bị
lưu trữ bền vững khác) là một vấn đề gây nhiều tranh cãi trong cộng đồng Java: Liệu
“persistence” là vấn đề đã được giải quyết bởi công nghệ quan hệ và các mở rộng (ví dụ
như các stored procedure) hay “persistence” là một vấn đề lớn cần phải được giải quyết
bởi các mô hình thành phần Java đặc biệt như là các EJB entity bean; Ta nên code tay
ngay cả các thao tác cơ bản nhất như create, read, update, delete trong SQL hay JDBC,
hay các công việc này nên được tự động hóa; Làm thế nào để hệ thống đạt được tính khả
chuyển đổi nếu mỗi hệ thống quản trị cơ sở dữ liệu (DBMS) lại có một hình thái ngôn
ngữ của riêng nó (có các token, syntax, semantic riêng); Có nên từ bỏ SQL hoàn toàn và
chuyển sang một công nghệ cơ sở dữ liệu mới, ví dụ như các hệ thống cơ sở dữ liệu đối
tượng. Cuộc tranh luận vẫn còn tiếp tục nhưng hiện nay đã có một giải pháp nhận được
sự chấp thuận ngày càng tăng. Giải pháp này có tên gọi là ánh xạ đối tượng/quan hệ
(ORM – object/relational mapping). Hibernate là một sự hiện thực ORM mã nguồn mở.
Hibernate là một dự án với mục tiêu trở thành một giải pháp hoàn chỉnh cho vấn
đềquản lý dữ liệu persistent trong Java. Nó làm trung gian cho các tương tác của ứng
dụng với một cơ sở quan hệ, giải phóng nhà phát triển để họ có thể tập trung vào các vấn
đề nghiệp vụ. Hibernate kkông phải là một giải pháp bắt buộc (non-intrusive solution).
Nghĩa là ta không cần phải theo các luật cụ thể cho Hibernate và các design pattern khi
viết các logic nghiệp vụ và các class persistent. Do đó Hibernate tích hợp tốt với hầu hết
các ứng dụng mới và đang tồn tại mà không yêu cầu những thay đổi đối với phần còn lại
của chương trình.
1.1 ORM (Objet – relation Mapping)
Một ánh xạ đối tượng/quan hệ (object/relational mapping) là một sự thường trú tự
động (persistence) của các đối tượng thuộc một ứng dụng Java trong các table thuộc một
cơ sở dữ liệu quan hệ bằng cách sử dụng các metadata mô tả sự ánh xạ giữa các đối
tượng và cơ sở dữ liệu. Về bản chất, ORM, làm việc thông qua sự chuyển đổi dữ liệu từ
một dạng thể hiện này sang một dạng khác.
Một giải pháp ORM cấu tạo bởi bốn phần sau:
2
• Một API để thực hiện các giao thức CRUD cơ bản trên các đối tượng của các
class persistent.
• Một ngôn ngữ hoặc API để thực hiện các câu truy vấn trên các class và các
thuộc tính của class.
• Một tiện ích để xác định các ánh xạ metadata.
• Một kỹ thuật cho việc hiện thực ORM để tương tác với các đối tượng giao dịch
để thực hiện dirty checking, lazy association fetching và các chức năng tối ưu
khác.
1.2 Kiến trúc Hibernate
1.2.1 Interface
Các interface lập trình (programming interface) là điều đầu tiên cần biết về
Hibernate để sử dụng nó trong tầng persistencecủa ứng dụng của bạn. Một mục tiêu chính
của thiết kế API là giữ cho các interface giữa các thành phần phần mềm càng gần nhau
càng tốt. Tuy nhiên trong thực tế, các ORM API không hề ít. Tuy vậy, ta cũng không cần
phải hiểu tất cả interface Hibernate một lúc. Hình 1.1 mô tả các vai trò của của các
interface Hibernate quan trọng nhất trong tầng nghiệp vụ (business layer) và tầng
persistence (persistence layer). Tầng nghiệp vụ nằm trên tầng persistence, bởi vì tầng
nghiệp vụ hoạt động như một client của tầng persistence trong một ứng dụng phân lớp
truyền thống. Lưu ý rằng, một số ứng dụng đơn giản có thể không tách biệt logic nghiệp
vụ khỏi logic persistence. Điều đó chỉ làm đơn giản lược đồ hơn.
Hình 1.1. Tổng quan về Hibernate API trong kiến trúc phân tầng
3
Các interface Hibernate được thể hiện trên hình trên có thể được phân thành các loại
chính sau:
• Các interface được các ứng dụng gọi để thực hiện các thao tác cơ bản CRUD
và các lệnh truy vấn. Chúng bao gồm Session, Transaction và Query.
• Các interface được code cơ sở ứng dụng gọi để cấu hình Hibernate, trong đó
quan trọng nhất là interface Configuration.
• Các interface Callback – là các interface cho phép ứng dụng phản hồi lại các
sự kiện (event) xảy ra bên trong Hibernate, gồm các interface như
Interceptor, Lifecycle và Validatable.
• Các interface giúp mở rộng các chức năng ánh xạ của Hibernate, gồm các
interface như UserType, CompositeUserType, và IdentifierGenerator. Các
interface này được hiện thực bằng code cơ sở ứng dụng.
Hibernate sử dụng các API Java có sẵn, bao gồm JDBC, JTA, JNDI. JDBC cung cấp
một trừu tượng hóa ban đầu các chức năng thông dụng của cơ sở dữ liệu quan hệ, cho
phép hầu hết các cơ sở dữ liệu với một JDBC driver được Hibernate hỗ trợ. JNDI và JTA
cho phép Hibernate tích hợp với J2EE server ứng dụng.
1.2.2 Một số interface chính
Sử dụng các interface này, bạn có thể lưu trữ và rút trích các đối tượng persistent và
điều khiển các transaction.
- Session interface:
o Một thực thể Session ít tốn tài nguyên (có tính lightweight), dễ dàng tạo
và hủy. Đặc điểm này khá quan trọng vì trong một ứng dụng cần tạo và
xóa các session mọi lúc, có thể là trên mỗi request. Các Hibernate session
không có tính an toàn về thread (not threadsafe) nên được thiết kế để chỉ
được một thread sử dụng một thời điểm.
o Định nghĩa của Hibernate về một session là một cái giữa connection và
transaction. Có thể hình dung về session như một bộ đệm hoặc một tập
hợp các đối tượng đã được nạp vào có liên quan đến một đơn vị công
việc. Hibernate có thể dò tìm ra những thay đổi các đối tượng trong đơn
vị công việc này. Nó cũng là interface cho các hàm liên quan đến
persistent, ví dụ như là việc lưu và rút trích đối tượng.
- SessionFactory interface
o Ứng dụng tạo được một thực thể Session từ một SessionFactory.
SessionFactory không có tính lighweight. Nó được dùng chung cho nhiều
thread ứng dụng. Điển hình là có một SessionFactory cho toàn ứng dụng,
được tạo trong phần khởi tạo ứng dụng. Tuy nhiên, nếu ứng dụng cần truy
xuất nhiều database bằng cách sử dụng Hibernater thì cần một
SessionFactory cho mỗi database.
o SessionFactory lưu lại các câu lệnh SQL đã tạo và các siêu dữ liệu
(metadata) ánh xạ khác mà Hibernate sử dụng ở thời điểm chạy. Nó cũng
4
lưu các dữ liệu đã từng được đọc (được cache lại) trong một đơn vị công
việc và có thể được sử dụng trong một đơn vị công việc trong tương lai
(chỉ nếu các ánh xạ class và collection xác định rằng cache cấp hai này là
cần thiết).
- Configuration interface
Đối tượng Configuration được sử dụng để định cấu hình và boostrap
Hibernate. Ứng dụng sử dụng một thực thể Configuration để xác định vị trí của
các tài liệu ánh xạ (mapping document) và các thuộc tính xác định Hibernate và
sau đó tạo ra SessionFactory.
- Transaction interface
o Interface Transaction là một API tùy chọn. Nghĩa là các ứng dụng
Hibernate có thể không sử dụng interface này, thay vào đó nó sẽ quản lý
các transaction bằng code cơ sở của riêng nó. Một Transaction interface
tách biệt (trừu tượng hóa) code ứng dụng khỏi sự hiện thực transaction
bên dưới (có thể là transaction JDBC, UserTransaction JTA hoặc
transaction CORBA) cho phép ứng dụng điều khiển các biên transaction
(transaction boundary – cho phép xác định khi nào một transaction bắt
đầu và kết thúc) thông qua một API nhất quán.
o Điều này giúp cho các ứng dụng Hibernate có tính khả chuyển trên các
môi trường thực thi và các container khác nhau.
- Query và Criteria interface
o Interface Query cho phép thực hiện các truy vấn đến các cơ sở dữ liệu và
điều khiển việc các truy vấn này được thực thi như thế nào. Các truy vấn
được viết bằng HQL (Hibernate Query Language) hoặc bằng ngôn ngữ
SQL nguyên thuỷ. Một thực thể Query được dùng để bind các thông số
truy vấn, giới hạn của số kết quả trả về bởi truy vấn và nhiệm vụ cuối
cùng là thực thi truy vấn.
o Interface Criteria cũng tương tự như vậy, nó cho phép tạo và thực thi các
truy vấn chuẩn hướng đối tượng.
o Một thực thể Query có tính lightweight và không thể sử dụng ngoài
Session đã tạo ra nó.
1.2.3 Các interface callback
Interface Callback cho phép các ứng dụng nhận một thông báo khi có một sự kiện
xảy ra đối với một đối tượng – ví dụ như khi đối tượng được nạp vào, được lưu hoặc bị
xoá. Các ứng dụng Hibernate không cần hiện thực những callback này nhưng chúng sẽ có
ích cho việc hiện thực các loại chức năng tổng quát, như là tạo ra các bản ghi kiểm toán
(audit record).
5
Gồm các interface như:
- Lifecycle và Validatable: cho phép một đối tượng persistent phản hồi các sự kiện
liên quan đến vòng đời (lifecycle) persistent của riêng nó.
- Interceptor cho phép ứng dụng xử lý các callback mà không buộc các class
persistent hiện thực các API đặc trưng của Hibernate (Hibernate-specific API).
Cáchiện thực interface Interceptor được truyền đến các thực thể persistent như
các thông số.
1.2.4 Type
Một thành phần cơ bản và rất mạnh của kiến trúc này định nghĩa của Hibernate về
Type. Một đối tượng Type của Hibernate ánh xạ một kiểu của Java đến một kiểu cột của
cơ sở dữ liệu (database column type). Kiểu này có thể kéo dài qua nhiều cột. Tất cả các
thuộc tính persistent của class persistent, bao gồm các quan hệ (association), có môt kiểu
Hibernate tương ứng. Điều này làm cho Hibernate trở nên đặc biệt linh hoạt và dễ mở
rộng.
Có rất nhiều kiểu Hibernate đã được xây dựng sẵn, bao gồm tất cả các kiểu gốc
củaJava (Java primitive) và nhiều class JDK, bao gồm các kiểu cho java.util.Currency,
java.util.Calendar, byte[] và java.io.Serializable.
Hơn thế nữa, Hibernate còn hỗ trợ cho kiểu tuỳ chỉnh do người dùng tự định nghĩa
(user-defined custom type). Các interface UserType và CompositeType cho phép thêm
vào kiểu riêng của người sử dụng. Đặc tính này giúp điều khiển các class ứng dụng được
sử dụng rộng rãi như là Address, Name, MonetaryAmount một cách thuận tiện.Các kiểu
tuỳ chỉnh được xem như là một đặc tính chính của Hibernate và được khuyến khích dùng.
1.2.5 Các interface mở rộng
Nhiều chức năng mà Hibernate cung cấp có khả năng định cấu hình. Điều này cho
phép ta chọn giữa nhiều chiến lược được xây dựng sẵn. Khi các chiến lược này không đủ
hiệu quả, Hibernate cho phép thêm vào các hiện thực của riêng người dùng bằng cách
hiện thực một interface. Các điểm mở rộng gồm có:
• Tạo khoá chính: interface IdentifierGenerator.
• Hỗ trợ hình thái ngôn ngữ SQL: class trừu tượng Dialect.
• Các chiến lược lưu trữ đệm – cache: interface Cache và CacheProvider.
• Quản lý kết nối JDBC: interface ConnectionProvider.
• Quản lý transaction: interface TransactionFactory, Transaction và
TransactionManagerLookup.
• Các chiến lược ORM: phân cấp interface ClassPersister.
• Các chiến lược truy cập thuộc tính: interface PropertyAccessor.
• Tạo proxy: interface ProxyFactor.
6
1.3 Truy vấn trong Hibernate - HQL (Hibernate Query Language)
1.3.1 Định nghĩa
Hibernate cũng có một ngôn ngữ truy vấn cho riêng nó, tên là Hibernate Query
Language, gọi tắt là HQL. Về mặt cấu trúc HQL cũng tương tự như SQL với các mệnh đề
SELECT, FROM và WHERE. HQL cũng hỗ trợ subselect nếu cơ sở dữ liệu bên dưới
cũng hỗ trợ. Nhưng thật sự HQL là ngôn ngữ truy vấn hướng đối tượng với các khái niệm
về thừa kế (inheritence), tính đa hình (polymorphism) và quan hệ association).
1.3.2 Các cách thực hiện việc truy vấn trong Hibernate
Thực ra trong Hibernate ta có thể dùng đến 3 cách để biểu diễn câu truy vấn.
- Cách thứ nhất là sử dụng HQL:
Ví dụ:
session.createQuery(“from Category c where c.name like ‘Laptop%’”);
- Cách thứ hai là sử dụng hàm API Criteria cho truy vấn, gồm truy vấn theo chuẩn (query
by criteria, QBC) và truy vấn theo ví dụ (query by example, QBE).
Ví dụ:
session.createCriteria((Category.class).add(Expression.like("name","Laptop
%"));
- Cách thứ ba là sử dụng câu SQL trực tiếp với sự ánh xạ tự động tập kết quả thành các đối
tượng.
Ví dụ:
session.createSQLQuery("select {c.*} from CATEGORY {c} where
NAME like 'Laptop%'","c",Category.class);
Trong tài liệu này chúng ta chỉ đề cập đến cách truy vấn đầu tiên. Nếu cần tham khảo
thêm về hai cách còn lại, các bạn có thể tham khảo thêm tài liệu Hibernate in Action,
chương 7 “Retrieving objects efficiently”.
1.3.3 Cách thực thi câu truy vấn
- Interface cho việc truy vấn (query interface)
Để tạo một thực thể Query mới ta có thể gọi hàm createQuery().
Ví dụ:
Query hqlQuery = session.createQuery("from User");
- Phân trang kết quả
Phân trang là một kỹ thuật thường được sử dụng. Người sử dụng có thể nhìn thấy
kết quả tìm kiếm (ví dụ, tìm các Nhânviên trong một công ty) trong một trang. Trang này
chỉ hiển thị giới hạn một tập con của kết quả (ví dụ, 10 nhân viên) vào một thời điểm, và
người sử dụng có thể chuyển sang trang kết quả kế tiếp hoặc quay lại trang kết quả trước
đó. Interface Query hỗ trợ việc phân trang của kết quả truy vấn:
Query query = session.createQuery(“from User u order by u.name asc”);
7
query.setFirstResult(0);
query.setMaxResult(10);
Hàm setMaxResult(10) giới hạn tập kết quả truy vấn đến 10 đối tượng đầu tiên được
chọn ra (select) trong cơ sở dữ liệu.
1.3.4 Các thành phần trong câu truy vấn
- Mệnh đề FROM
Mệnh đề FROM cho phép xác định các đối tượng sẽ được truy vấn. Nó cũng cho
phép tạo ra các bí danh cho tên đối tượng. Giả sử ta muốn truy vấn tất cả các thực thể
Event có tên là “Festival”, câu truy vấn sẽ như sau:
FROM Event e WHERE e.name = “Festival”
Câu truy vấn này có một cái tên bí danh cho thực thể Event là e. Tên bí danh này có
thể được sử dụng giống như trong câu truy vấn SQL ngoại trừ việc ở đây ta sử dụng nó
để định danh các đối tượng thay vì định danh các bảng như trong SQL. Một điểm cần lưu
ý trong câu truy vấn HQL trên là nó đang truy vấn trên một thuộc tính của đối tượng
Event: thuộc tính name. Như vậy là khi truy vấn ta không dùng tên cột trong bảng mà
dùng tên thuộc tính. Do HQL sử dụng theo các khái niệm hướng đối tượng, vì vậy không
thể viết tắt e.name thành name được. Tên thuộc tính phải luôn được đi kèm với tên đối
tượng. Rõ ràng là khi sử dụng HQL, ta không quan tâm đến table và column dữ liệu quan
hệ bên dưới mà tập trung vào các thuộc tính của các đối tượng.
Khi truy vấn chỉ liên quan đến một đối tượng, thì chỉ cần một đối tượng List chứa
các thực thể của đối tượng truy vấn. Nếu cần truy vấn nhiều đối tượng có liên quan đến
nhau (các đối tượng có quan hệ với nhau), mệnh đề FROM sẽ chứa nhiều đối tượng:
Ví dụ:
FROM Event e, Attendee a WHERE …
Và danh sách kết quả sẽ chứa một phép tích Cartesian các đối tượng được truy vấn
(phép tích Cartesian là kết quả của việc kết hợp mỗi dòng của môt bảng với mỗi dòng của
một bảng khác) mà rất có thể nó không phải là sản phẩm ta mong muốn. Để truy vấn trên
nhiều đối tượng có liên quan đến nhau, ta cần kết hợp (join) chúng trong mệnh đề
FROM.
- Kết hợp (Join)
Trong SQL cũng có phép JOIN trả về dữ liệu từ nhiều bảng với một câu truy vấn
duy nhất. Tuy nhiên phép JOIN trong HQL chỉ kết hợp các thuộc tính của đối tượng và
các quan hệ, trong khi phép JOIN trong SQL kết hợp các bảng.
Nếu cần truy vấn các thực thể của đối tượng Event mà một người tham dự
(Attendee) xác định sắp tham gia, ta phải kết hợp thuộc tính attendee với đối tượng Event
trong câu truy vấn:
FROM Event e join e.attendee a WHERE a.id = 321
Trong HQL, ta có thể kết hợp tất cả các quan hệ (nhiều-một và một-một), cũng như
các tập hợp với đối tượng cơ sở của truy vấn. Đối tượng cơ sở trong một truy vấn là đối
8
tượng hiện diện trong mệnh đề FROM. Trong ví dụ trên, đối tượng cơ sở là Event. Trong
ví dụ trên, ta có thể đặt một bí danh cho quan hệ được kết hợp và truy vấn trên các thuộc
tính trong đối tượng được kết hợp. Quy tắc đặt tên cho bí danh của HQL là sử dụng chữ
viết thường.
o Các loại kết hợp
HQL có nhiều kiểu kết hợp khác nhau, các kiểu kết hợp này đều có trong SQL, chỉ
có một kiểu kết hợp là ngoại lệ. Bảng sau tóm tắt các kiểu kết hợp
Loại kết hợp Luật
inner join Các đối tượng không phù hợp với nhau ở một trong hai đầu kết
hợp sẽ bị loại bỏ.
left (outer) join Trả về tất cả đối tượng ở đầu trái của kết hợp. Nếu đối tượng ở đầu
trái của kết hợp không có đối tượng nào trùng khớp (match) ở đầu
phải của kết hợp thì nó vẫn được trả về.
right (outer) join Trả về tất cả đối tượng ở đầu phải của kết hợp. Nếu đối tượng ở
đầu phải của kết hợp không có đối tượng nào trùng khớp (match) ở
đầu trái của kết hợp thì nó vẫn được trả về.
full join Trả về tất cả đối tượng ở cả hai đầu kết hợp, bất kể có hay không
đối tượng trùng khớp ở đầu đối diện của kết hợp.
inner join fetch Được sử dụng để truy xuất một đối tượng kết hợp hoặc một tập
hợp các đối tượng bất kể có hay không outer-join hoặc lazy
property trên quan hệ. Loại kết hợp này không có trong SQL.
Loại kết hợp mặc định được sử dụng là inner join. Muốn sử dụng loại khác ta phải
xác định chính xác loại kết hợp trong mệnh đề kết hợp (left, right, full). Tất cả các loại
kết hợp đều có loại tương ứng trong SQL, ngoại trừ inner join fetch. Kết hợp một lazy
collection với inner join fetch sẽ làm cho collection này được trả về khi populated.
Ví dụ:
FROM Event e INNER JOIN FETCH e.speakers
Câu truy vấn này sẽ trả về tất cả các thực thể Event với các tập hợp của Speakers
(nghĩa là một thực thể Event sẽ có một tập các Speaker tương ứng với Event đó).
Xét sự kết hợp một đối tượng có quan hệ một-nhiều với một đối tượng cơ sở:
FROM Event e JOIN e.location l WHERE l.name = :name
Kết hợp thực thể Location với đối tượng Event cho phép truy vấn trên các thuộc
tính của Location, và đạt được kết quả truy vấn hiệu quả hơn. Giải thích: Nếu không
dùng phép kết hợp join này ta có thể có câu truy vấn sau:
FROM Event e WHERE e.location.name = :name AND
e.location.address.state = :state
Vì ta phải duyệt qua các đối tượng hai lần, một lần cho tên của Location và một lần
cho trạng thái (state) của Location, do đó trình biên dịch truy vấn sẽ kết hợp thực thể
Location với Event hai lần. Kết hợp Location với Event trong mệnh đề FROM sẽ chỉ phải
9
thực hiện phép kết một lần và truy vấn cũng thực hiện hiệu quả hơn. Các đối tượng trong
phép kết cũng có thể được trả về trong mệnh đề SELECT của câu lệnh HQL.
- Mệnh đề SELECT
Mệnh đề SELECT cho phép xác định một danh sách các giá trị trả về từ câu truy
vấn. Giá trị trả về có thể là toàn bộ đối tượng, các thuộc tính xác định của đối tượng và
giá trị dẫn xuất từ một truy vấn. Giá trị dẫn xuất bao gồm kết quả từ các hàm chức năng,
như là hàm min(), max() và hàm count(). Một đặc điểm của HQL là khả năng trả về các
đối tượng mới từ các giá trị được chọn.
o Phép chiếu:
Phép chiếu là việc chọn các cột xác định của dữ liệu trả về từ một truy vấn.
Giả sử thay vì trả về toàn bộ đối tượng Event trong câu truy vấn, ta chỉ muốn lấy tên
của Event. Việc truy xuất toàn bộ đối tượng chỉ để lấy thuộc tính tên thì rất không hiệu
quả. Thay vào đó, câu truy vấn chỉ cần truy xuất đến dữ liệu mong muốn:
SELECT e.name FROM Event e
Câu truy vấn này trả về một danh sách các thực thể kiểu chuỗi chứa các tên của
Event. Nếu muốn truy xuất hai thuộc tính, ví dụ như ngày bắt đầu và tên của Event:
SELECT e.name, e.startDate FROM Event e
Mỗi phần tử trong danh sách trả về là một array kiểu Object (Object[]) chứa các
giá trị xác định. Chiều dài của array Object[] bằng với số cột được yêu cầu truy xuất. Sau
đây là một ví dụ về cách thức truy vấn và xử lý truy vấn trên nhiều thuộc tính.
Ví dụ:
Session session = factory.openSession();
String query = " select e.name, e.startDate from Event e ";
Query query = session.createQuery("query");
List results = query.list();
for (Iterator I = results.iterator(); i.hasNext();) {
Object[] row = (Object[]) i.next();
String name = (String) row[0];
Date startDate = (Date) row[1];
// …
}
Lưu ý rằng các giá trị trong array Object[] có thứ tự giống với thứ tự các thuộc
tính trong câu truy vấn. Array chứa các thực thể thuộc kiểu Object, không có giá trị thuộc
kiểu cơ bản (gồm các kiểu như int, long, boolean…) có thể được trả về từ một truy vấn
dạng này. Giới hạn này cũng vẫn tồn tại khi truy vấn một giá trị đơn, bởi vì một List
không thể chứa các giá trị kiểu cơ bản.
Một cách thông dụng để xử lý vấn đề này là dùng các đối tượng giản lược
(summary object) chứa một tập con dữ liệu của đối tượng. Trong trường hợp ví dụ trên,
một đối tượng giản lược sẽ chứa thuộc tính name và startdate của Event. Khi duyệt trên
danh sách kết quả, ta sẽ cần một danh sách cho các đối tượng giản lược. Có một cách tốt
hơn để giải quyết vấn đề trên.
o Trả về các đối tượng mới
10
Mệnh đề SELECT có thể được sử dụng để tạo ra các đối tượng mới, giữ các giá trị
trong câu truy vấn. Xét ví dụ sau:
select new EventSummary(e.name, e,startDate) from Event e
Danh sách kết quả sẽ được trả về theo các thực thể của đối tượng EventSummary. Dĩ
nhiên class EventSummary phải có một constructor tương ứng với constructor được sử
dụng trong câu lệnh HQL: EventSummary(String, Date).
Phần trước đã trình bày về những thành phần chính của câu truy vấn HQL. Phần sau
sẽ trình bày về các hàm tập hợp (aggregate functions) có sẵn trong HQL và cách chúng
được sử dụng trong mệnh đề SELECT và WHERE.
o Sử dụng các hàm
Các hàm là các lệnh đặc biệt, trả về một giá trị được tính toán. Trong SQL có hai
kiểu hàm: hàm kết hợp và hàm vô hướng (scalar). Các hàm vô hướng thường xử lý trên
một giá trị đơn và trả về một giá trị đơn. Cũng có các hàm vô hướng không cần đối số,
như hàm now() hoặc CURRENT_TIMESTAMP, cả hai đều trả về thời gian của hệ thống
(timestamp). Các hàm tập hợp xử lý trên một tập các giá trị trả về giá trị tổng kết
(summary value). Hibernate hỗ trợ năm hàm tập hợp trong SQL thông dụng nhất, gồm:
count (đếm phần tử trong tập hợp thỏa ràng buộc), avg (tính trị trung bình), min (tính trị
nhỏ nhất), max (tính trị lớn nhất) và sum (tính tổng các giá trị). Các hàm này có chức
năng tương tự như các hàm tương ứng trong SQL.
Xét ví dụ sau:
SELECT COUNT(e) FROM Event e
Ví dụ này trả về số đối tượng Event có trong cơ sở dữ liệu. Để đếm số đối tượng
Event và không đếm trùng các đối tượng giống nhau, sử dụng từ khóa distinct:
SELECT COUNT(DISTINCT e) FROM Event e
Tất cả các hàm tập hợp đều trả về một đối tượng Integer. Để truy xuất kết quả,
cách đơn giản nhất là lấy giá trị đầu tiên trong danh sách kết quả:
Integer count = (Integer) session.find (“select count(distinct e) from Event
e”).get(0);
Xét một ví dụ khác: lấy tập hợp những người tham gia (Attendee) cho một sự kiện
cho trước. Hiện nay để lấy được thông tin này thì code sẽ như sau:
String query = “from Event e inner join fetch e.attendees where e.name =
:name”;
Query q = session.createQuery(query);
q.setParameter(“name”, ”Meeting”);
Event event = (Event) q.list().get(0);
Set attendees = event.getAttendees();
session.close();
Có một cách khác ngắn hơn nhiều để đạt được tập hợp con. Hibernate cung cấp hàm
elements(…) để trả về các thành phần của một tập hợp:
SELECT elements(e.attendees) FROM Event e WHERE e.name = :name
Câu truy vấn trả về một đối tượng List chứa các thực thể Attendee ứng với sự kiện
Event. Nếu sử dụng phép kết hợp trong mệnh để FROM, ta có thể sử dụng alias trong
phép kết hợp làm đối số của hàm. Câu truy vấn sau cũng tương đương với câu trên:
11
SELECT elements(a) FROM Event e
JOIN e.attendees a
WHERE e.name =: name
Các hàm cũng có thể được sử dụng trong mệnh đề WHERE.
o Các thuộc tính có sẵn của HQL
Các thuộc tính có sẵn của HQL dành cho các đối tượng trong câu truy vấn. HQL hỗ
trợ hai thuộc tính đối tượng: id và class. Thuộc tính id cho phép truy xuất đến giá trị khóa
chính của một đối tượng trong cơ sở dữ liệu. Bất kể ta đặt tên cho thuộc tính id trong đối
tượng và trong định nghĩa ánh xạ là gì thì sử dụng id trong câu truy vấn HQL vẫn sẽ trả
về giá trị khóa chính. Ví dụ, nếu có một class với thuộc tính id được đặt tên là objectId, ta
vẫn có thể sử dụng thuộc tính id trong câu truy vấn HQL:
FROM MyObject m WHERE m.id > 50
Ta vẫn có thể sử dụng thuộc tính objectId nếu muốn. Có thể xem thuộc tính Id của
HQL như một cách viết gọn cho giá trị khóa chính.
Thuộc tính class cung cấp chức năng tương tự. Thuộc tính class giúp truy xuất đến
tên class Java của các đối tượng trong cơ sở dữ liệu. Điều này đặc biệt có ích khi ta ánh
xạ một phân cấp class trong cơ sở dữ liệu và chỉ muốn trả về các class thuộc một kiểu
xácđịnh. Xét ví dụ sau để thấy thuộc tính class được sử dụng như thế nào.
Ví dụ class Attendee có quan hệ với class Payment. Class Payment xác định cách
Attendee sẽ trả tiền cho các Event. Payment có hai class con (subclass):
CreditCardPayment và CashPayment. Câu truy vấn sau sẽ truy xuất được tất cả Attendee
trả bằng tiền mặt:
FROM Attendee a JOIN a.payment p WHERE p.class = de.laliluna.CashPayment
Cũng giống như thuộc tính id, ta cũng có thể trả về thuộc tính class trong lệnh
SELECT
SELECT p.id, p.class FROM Payment p;
Câu truy vấn trả về tất cả các id và class cho đối tượng Payment trong một đối tượng
List gồm các phần tử là array Object[]. Tuy nhiên, thuộc tính class không được trả về như
là một thực thể của java.lang.Class. Thay vào đó, thuộc tính class là môt thực thể
java.lang.String, có giá trị là giá trị được xác định trong thuộc tính discriminor-value
(tạm dịch là giá trị phân biệt) trong định nghĩa ánh xạ.
Thuộc tính class chỉ có trong phân cấp đối tượng. Nói cách khác là thuộc tính class
chỉ có trong các đối tượng được ánh xạ với một discriminator-value. Nếu truy xuất thuộc
tính class của một đối tượng không có discriminator-value sẽ nhận ngoại lệ
QueryException báo rằng bộ phân tích truy vấn (query parser) không thể phân tích được
thuộc tính class này.
Các thuộc tính id và class có thể được sử dụng trong mệnh đề SELECT và WHERE.
o Sử dụng biểu thức
Các biểu thức được sử dụng để truy vấn các đối tượng có thuộc tính thỏa mãn các
tiêu chuẩn xác định. Được sử dụng trong mệnh đề WHERE của truy vấn, các biểu thức
hỗ trợ hầu hết các toán tử thông dụng trong SQL.
12
Tóm lại: Hibernate là một công nghệ mới giải phóng cho người phát triển code khỏi
cơ sở dữ liệu quan hệ thực sự bên dưới. Người phát triển chỉ cần biết các class ánh xạ từ
cơ sở dữ liệu bên dưới mà không phải làm việc với các bảng (table).
2 Spring MVC
2.1 Mô hình MVC trong ứng dụng web
MCV (Model View Controller) là tên một phương pháp chia nhỏ một ứng dụng
thành ba thành phần để cài đặt, mỗi thành phần đóng một vai trò khác nhau và ảnh
hưởng lẫn nhau, đó là models, views, và controllers.
Models (Tầng dữ liệu): là một đối tượng hoặc một tập hợp các đối tượng biểu diễn
cho phần dữ liệu của chương trình, ví dụ như các dữ liệu được lưu trong database, dữ
liệu từ một hệ thống các ứng dụng khác như legacy system, file system, mail system
…
Views (Tầng giao diện) : là phần giao diện với người dùng, bao gồm việc hiện dữ
liệu ra màn hình, cung cấp các menu, nút bấm, hộp đối thoại, chọn lựa …, để người
dùng có thể thêm, xóa. sửa, tìm kiếm và làm các thao tác khác đối với dữ liệu trong
hệ thống Thông thường, các thông tin cần hiển thị được lấy từ thành phần Models.
13
Controllers (Tầng điều khiển): chịu trách nhiệm xử lý các tác động về mặt giao
diện, các thao tác đối với models, và cuối cùng là chọn một view thích hợp để hiển
thị ra màn hình. Trong kiến trúc MVC, view chỉ có tác dụng hiển thị giao diện mà
thôi, còn điều khiển dòng nhập xuất của người dùng vẫn do Controllers đảm trách.
2.2 Giới thiệu về Spring framework
Spring Framework, hay ngắn hơn là Spring, là một cấu trúc dùng để xây dựng
chương trình ứng dụng mã nguồn mở dành cho ngôn ngữ lập trình Java.
Spring Framework cung cấp các giải pháp cho nhiều thách thức kỹ thuật mà các
nhà phát triển phần mềm Java và các tổ chức muốn tạo ứng dụng dựa trên nền
tảng Java phải đối mặt với Java platform. Bởi vì số lượng lớn chức năng của
Spring Framework, việc phân loại những bộ phận cơ bản kiến tạo nên bộ khung
là một việc rất khó. Spring Framework không chỉ được liên kết riêng biệt với
Java Enterprise platform mặc dù sự tích hợp sâu của Spring trong Java EE là
một yếu tố quan trọng cho sự phổ dụng, nổi tiếng của nó.
Có lẽ Spring Framework nổi tiếng nhất với sự cung cấp một cách hiệu quả các
tính năng cần thiết để tạo ra các ứng dụng thương mại phức tạp, vượt lên khỏi
các mô hình lập trình từng chiếm vị trí ưu thế trong lịch sử ngành công nghiệp
phần mềm. Tiếp theo, nó cũng được ghi nhận về việc đưa các chức năng vốn ít
được quen biết vào thực tế, có khi còn vượt cả ra bên ngoài nền tảng Java nữa.
Thành quả của việc này là nó đã tạo nên một bộ khung, cung cấp một mô hình
nhất quán và làm cho nó có thể áp dụng vào hầu hết các loại ứng dụng được tạo
trên nền tảng Java ngày nay. Bộ khung Spring được coi như thực thi một cách
làm việc dựa trên những thói quen thực hành tốt nhất và những tiêu chuẩn công
nghiệp, và đồng thời tạo cơ hội cho những lĩnh vực trong Java sử dụng nó
2.2.1 IoC(Inversion of Control)
Cung cấp khung để cài đặt và quản lý các đối tượng Java. Khung này có nhiệm
vụ quản lý vòng đời của đối tượng: tạo đối tượng, gọi các hàm khởi tạo, cấu
hình các đối tượng bằng cách nối nó lại với nhau.
Đối tượng được tạo bởi khung này được gọi là đối tượng được quản lý hay
Bean. Thông thường Bean được định nghĩa trong file cấu hình XML
2.2.2 DI(Dependency Injection)
Là một mẫu hình mà trong đó khung chứa truyền giao các đối tượng cho nhau
bằng tên của chúng, thông qua những phương pháp khởi tạo (constructor), các
đặc tính (properties) hoặc các phương pháp chế xuất (factory methods /
Factory method pattern).Ví dụ:
14
Có thể cấu hình properties, constructor bằng XML:
<? xml version = "1.0" encoding = "UTF-8" ?>
< beans xmlns = "
xmlns:xsi =
xsi:schemaLocation = "
>
< bean id = "springQuizMaster" class = "SpringQuizMaster" >
</ bean >
< bean id = "strutsQuizMaster" class = "StrutsQuizMaster" >
</ bean >
< bean id = "quizMasterService" class = "QuizMasterService" >
< property name = "quizMaster" >
< ref local = "springQuizMaster" />
</ property >
</ bean >
</ beans
2.3 Giới thiệu về Spring MVC
Spring's Web MVC framework được thiết kế xung quanh một DispatcherServlet t,nó
gửi các request đến các handler, vớiviệc cho phép cấu hình các handler mapping, view
resolution, locale and theme resolution như là sự hỗ trợ tốt nhất choviệc upload file.
Hanler mặc định rất đơn giản “Controller interface”, chỉ đưa ra một phương thức
ModelAndViewhandleRequest(request,response). Cái này đã có thể được sử dụng cho
các controller của ứng dụng, nhưng sẽ thích hơnkhi bao gồm kiến trúc thực thi có thứ
bậc, sự nhất quán, ví dụ AbstractController, AbstractCommandController
andSimpleFormController. Các controller của ứng dụng sẽ là các lớp con tiêu biểu
của chúng . Chú ý rằng chúng ta có thểchọn một lớp cơ sở nếu chúng ta không có một
15
form, chúng ta không cần một form controller. Đây là điều khác biệt chínhso với
Strusts.
Spring Web MVC cho phép chúng ta sự dụng vài đối tượng như là một lệnh hoặc đối
tượng form – không cần implementmột framework-specific interface hoặc base class.
Spring's data binding có tính mềm dẻo cao: ví dụ, nó đối xử các kiểukhông hợp như
là các validation error, điều này có thể được ước lượng bởi ứng dụng, không như các
system error. Vớitất cả nhửng điều này có nghĩa là chúng ta không cần sao chép các
property của đối tượng bussiness, không cần phảigõ các chuỗi vào trong form đối
tượng chỉ để xử lí yêu cầu không phù hợp, hoặc chuyển đổi thành các chuỗi. Thay vào
đó nó thường đưa ra sự liên kết trực tiếp đến đối tượng business của chúng ta. Đậy là
điểm khác biệt chính so vớiStruts, Strust được xây dựng xung quanh yêu cầu các lớp
cơ sở như là Action và ActionForm.So sánh với WebWork , spring có thêm các vai
trò khác của đối tượng. Nó hỗ trợ thể sự thông báo của một controller,một lệnh tùy
chọn hoặc một đối tượng form, và một mô hình để thông đến view. Mô hình sẽ đơn
giản bao gồm lệnh và đốitượng form nhưng cũng có dữ liệu liên quan tùy ý; Thay vào
đó, một WebWork Action kết hợp tất cả những vai trò đóvào trong một đối tượng
riêng rẽ. WebWork cho phéo chúng ta sử dụng những đối tượng business của form,
nhưng chỉ làmcác thuộc tính bean cho các lớp Action tương ứng. Cuối cùng, những
thể hiện Action giống nhau, sự xử lí request của nóđược sử dụng cho việc đánh giá và
xũa định vị trí của form trong View. Như vậy, dữ liệu liên quan cũng cần được
môhình như các thuộc tính bean của Action. Với những thứ như vậy, người ta có thể
cho rằng có quá nhiều vai trò cho mộtđối tượng.
Một sự thực thi của một Controller có thể thậm chi có thể ghi một view trựctiếp vào
response ( bằng cách trả về giá trị null null cho ModelAndView). Trong trường hợp
đơn giản, một thể hiệnModelAndView bao gồm một tên của view và một mô hình
ánh xạ(
Map), nó chứa đựng tên bean và những đối tượngtương ứng (going một lệnh hoặc một
form, chứa đựng dữ liệu liên quan). Giải pháp dùng tên view có thể được cấu hìnhtốt,
hoặc thông qua tên bean, một file properties, hoặc qua thông ViewResolver
implementation của chúng ta. Thật sự, môhình (Min MVC) được dựa trên Map
interface, nó cho phép các công nghệ view hoàn toàn trừu tượng. Một vài renderer có
thể được tích hợp trực tiếp, như là JSP, Velocity…. Mô hình Map được chuyển đổi
một cách đơn giản thành một địnhdạng thích hợp, như là các thuộc tính JSP request hoặc một
mẫu Velocity.
2.4 Một số thành phần quan trọng trong Spring MVC
16
2.4.1 DispatcherServlet
Spring's web MVC framework giống như nhiều web MVC frameworks, request-
driven, được thiết kế xung quanh một servlet trung tâm, nó gửi các request đến các
controller và đưa ra những chức năng dễ dàng cho sự phát triển củaứng dụng web.
Spring DispatcherServlet bất cứ như thế nào , làm nhiều hơn là chỉ có vậy. Nó hoàn
toàn được tích hợpvới Spring IoC container và như vậy cho phép chúng ta sự dụng bất
cứ đặc tính nào mà Spring có.
Luồng xử lí request của Spring Web MVC DispatcherServlet được minh họa trong
lược đồ bên dưới. Người đọccó hiểu biết về mô hình sẽ nhận thấy rằng that the
DispatcherServlet là một sự diễn tả của mẫu thiết kế “Front Controller”(đây là một
mẫu mà Spring Web MVC chia sẽ với nhiều web frameworks dẫn đầu khác )
DispatcherServlet là một Servlet thật (nó kế thừa từ lớp cơ sở HttpServlet ), và như
vậy được khai báo trongweb.xml của ứng dụng web của chúng ta. Những request mà
chúng ta muốn DispatcherServlet xử lí sẽ được ánh xạ, sử dụng một URLmapping
trong cùng file web.xml. Đây là chuẩn cấu hình của J2EE servlet; một ví dụ như là
một sự khai báo của DispatcherServletvà mapping có thể được tìm thấy bên dưới.
<web-app>
<servlet>
<servlet-name>example</servlet-name>
<servlet-
17
class>org.springframework.web.servlet.DispatcherServlet</servlet-
class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>example</servlet-name>
<url-pattern>*.form</url-pattern>
</servlet-mapping>
</web-app>
Trong ví dụ trên, tất cả các request kết thúc với .form sẽ được xử lí bởi 'example'
DispatcherServlet. Đây chỉ là bước khởi đầu để cấu hình trong sự thiết lập Spring
Web MVC Những bean khác nhau được sử dụng bởi Spring WebMVC framework
(trên và DispatcherServlet trên của chính nó) cầu được cấu hình ngay.
Framework sẽ, trên sự khởi tạo của một DispatcherServlet, tìm kiếm một tên file
[servlet-name]-servlet.xml
trong thư mục WEB-INF của ứng dụng web của chúng ta và tạo các bean được định
nghĩa ở đó (overriding những sự định nghĩa của một vài bean đượcđịnh nghĩa cùng
tên trong phạm vi global).
18
Spring DispatcherServlet có một sự ghép nối với các bean đặc biệt, nó dùng để có
thể xử lí các request và tạo ra những viewthích hợp. Những bean đó được bao gồm
bên trong Spring framework và có thể được cấu hình trong WebApplicationContext,
chỉ như một vài bean khác cần được cấu hình. Mỗi cái bean này được mô tả chi tiết
hơn bên dưới. Bây giờ, chúng ta sẽ chỉ đề cậpđến chúng, vừa để chúng ta biết chúng
tồn tại và có thể cho phép chúng ta nói về DispatcherServlet. Hầu hết các bean, có thể
thấyđược mặc định được cung cấp vì thế chúng ta không (initially) phải lo lằng về
làm sao cấu hình chúng.
Khi một DispatcherServlet được cài đặt cho việc sử dụng và một request đến
DispatcherServlet đặt biệt, người ta bảo rằngDispatcherServlet bắt đầu xử lí các
request. Danh sách các mô tả sau mô tả đầy đủ một request đến khi được sử lí bởi
mộtDispatcherServlet:
1. WebApplicationContext được tìm kiếm và bao trong request như một thuộc
tính để cho controller và những nguyên tốkhác trong quá trình xử lí sử dụng. Nó được
bao mặc định bởi khóa DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE.
2. Vị trí của locale resolver được bao với request để các nguyên tố trong trong quá
trình xử lí giải quyết locale để sử dụngkhi xử lí request (tạo view, chuẩn bị dữ
liệu,v.v ) nếu chúng ta không sử dụng resolver, nó sẽ không gây ảnh hưởng gì cả,
vìthế nếu chúng ta không cần locale giải quyết , chúng ta không phải xử dụng nó.
3. Theme resolver được bao với request để các yếu tố như là các views xác đinh
theme nào được sử dụng. Themeresolver không ảnh gì cả nếu chúng ta không dùng
nó, vì thế chúng ta không cần theme thì chỉ cần chúng ta làm ngơ với nó.
4. Nếu một multipart resolver được đặc tả, request được duyệt cho các multipart;
nếu các multipart được tìm thấy, requestđược bao phủ trong một
MultipartHttpServletRequest cho quá trình xử lí xa hơn nữa bởi các yếu tố bên trong
xử lí. (XemSection 13.8.2, “Using the MultipartResolver” thêm thông tin về
multipart handling).
5. Một handler thích hợp được tìm kiếm. Nếu một handler được tìm thấy, sự xử lí
buộc liên kết với handler (cácpreprocessor, các postprocessor, và controller) sẽ được
thực thi để chuẩn bị một a model (cho sự phát sinh [render]).
6. Nếu một model được trả về, view được phát sinh (render). Nếu không có mô
hình nào được trả về (cái có thể được quy vào một pre- or postprocessor, phân hoạch
request, ví dụ, cho lí do bảo mật), không có view được phát sinh (render), từ khi
request có thể đã được đầy đủ
19
Những ngoại lệ được quăng ra trong quá trình xử lí request nhận được một cách tình
có bởi một vài handler exception resolver màchúng được khai báo trong
WebApplicationContext. Sự dụng những exception resolver đó cho phép chúng ta
định nghĩa những hành vimột cách tùy biến trong trường hợp như các exception có
được bởi sự quăng ném.Spring DispatcherServlet cũng hỗ trợ trả về last-modification-
date, như là được đặc tả bởi Servlet API. Tiến trình của sự xác địnhngày chỉnh sửa
cuối cùng cho một request đặc biệt thì không phức tạp: DispatcherServlet sẽ tìm kiếm
một handler mapping thích hợp và kiểm thử nếu handler được tìm thấy interface
implements the interface LastModified . Nếu thế, giá trị của phương
thứcgetLastModified(request) của interface LastModified được trả về cho client.
2.4.2 Controllers
Sự thông báo của một controller là một phần trong thiết kế của mô hình MVC (Chính
xác hơn nó là 'C' trong MVC. Các Controller cung cấp chế độ truy cập vào ứng dụng
mà nó được định nghĩa tiêu biểu bởi một service interface. Các Controller giải thích
user input và chuyển đổi thành dạng input có thể hiểu được mà nó sẽ được trình diễn
cho user bằng view. Spring đã thực thi sự thôngbáo của một controller trong một cách
chung chung trừu tượng, cho phép một lượng lớn các loại controller được tao tạo ra.
Springchứa đựng đặc tả form (form-specific) của các controller, các command-based
controller, và các controller mà nó thực hiện wizard-style logic, thành tên nhưng một
ít.
Kiến trúc controller là interface org.springframework.web.servlet.mvc.Controller,
Source code cho nó đượcliệt kê bên dưới.
public interface Controller
{
/*** Process the request and return a ModelAndView object which the
DispatcherServlet* will render.*/
ModelAndView handleRequest(HttpServletRequest request,HttpServletResponse
response) throws Exception;
}
Như chúng ta có thể thấy, Controller interface định nghĩa một phương thức đơn độc
chịu trách nhiệm cho việc xử lí một request và trả vềmột model và view thích hợp. Ba
khái niệm đó là cơ sớ cho Spring MVC implementation - ModelAndView và
Controller. Trong khiController interface là khá trừu tượng, Spring đưa ra một lượng
các Controller implementation có thể sử dụng dễ dàng và nó đãchứa đựng một lượng
các chức năng mà chúng ta cần.Controller interface chỉ định nghĩa yêu cầu trách
20
nhiệm cơ bản cho mỗi controller;là xử lí một request và trả về một model và một
view.
2.4.3 Handler mappings
Sự dụng một handler mapping chúng ta có thể ánh xạ một web request đến một
handler thích hợp. Có một vài handler mappingchúng ta có thể sử dụng một cách tự
nhiên, ví dụ: SimpleUrlHandlerMapping hoặc BeanNameUrlHandlerMapping.
Chức năng của một HandlerMapping cơ bản cung cấp là sự phân phối của một
HandlerExecutionChain, Nó cần chứa handler phùhợp với request, và cũng có thể
chứa đựng một danh sách các handler chặn mà được áp dụng cho request. Khi một
request vào,DispatcherServlet sẽ điều khiển nó thông qua handler mapping để phân
tích và kiểm tra request và mang lên với mộtHandlerExecutionChain thích hợp. Sau
đó DispatcherServlet sẽ thực thi handler và các bộ chặn trong chuỗi thực thi (nếu có).
Khái niệm configurable handler mappings là có thể tùy ý chứa đựng các bộ chặn (thực
thi trước khi và sau khi handler thực sựđược thực thi, hoặc cả hai) là thực sự mạnh
mẽ. Khỗi lượng các chức năng được hỗ trợ có thể được xây dựng trong việc xây
dựngcác HandlerMapping tùy ý. Cách xử lí của một handler mapping tùy ý là một
handler không chị dựa trên URL của request mà nócũng mô tả trạng thái của session
liên kết với request.
Phần này mô tả hai handler mapping thông dụng. Chúng mở rộng từ
AbstractHandlerMapping và cùng có những thuôc tính sau:
• interceptors: danh sách các bộ chặn để dùng. Xem thêmSection 13.4.3,
“Intercepting requests - the HandlerInterceptor interface”.
• defaultHandler: default handler để dùng, Khi handler mapping này không hợp với
handler nào.
• order: dựa trên giá trị của thuộc tính order (xem org.springframework.core.Ordered
interface), Spring sẽ sắp xếp tất cảcác handler mappings đang có trong context và áp
dụng handler đầu tiên.
alwaysUseFullPath: nếu thuộc tính này được set là true, Spring sẽ sự dụng đường dẫn
đầy đủ servlet context hiện tại đểtìm handler thích hợp. Nếu thuộc tính này được set là
false (mặc định), đường dẫn bên trong servlet mapping hiện tại sẽđược sử dụng . Ví
dụ, Nếu một servlet được ánh xạ sử dụng /testing/* và alwaysUseFullPath được set là
true,/testing/viewPage.html sẽ được sử dụng, Trái lại nếu thuộc tính được set là
false, /viewPage.html sẽ được sử dụng.
• urlPathHelper: sử dụng thuộc tính này, chúng ta có thể sử dụng UrlPathHelper khi
kiểm tra URLs. Bình thường, Chúng ta không nên thay đổi giá trị mặc định.
• urlDecode: Giá trị mặc định của thuộc tính này là false. HttpServletRequest trả về
request URLs và URIs mà không đượcdecoded. Nếu chúng ta không muôn chúng
được mã hóa trước khi HandlerMapping sự dụng chúng để tìm một handler thíchhợp ,
21
chúng ta phải đặt thuộc tính này là true (nó yêu cầu JDK 1.4). Phương thức decode sử
dụng hoặc là encoding được đặctả bởi request hoặc default ISO-8859-1 encoding .
• lazyInitHandlers: allows for lazy initialization of singleton handlers (prototype
handlers are always lazily initialized). Defaultvalue is false
2.4.4 Một số thành phần khác
HandlerAdapter:Thực thi đối tượng nắm giữ yêu cầu đến
View: trả về response tới client
ViewResolver: tất cả các MVC framework đều cung cấp resolver để định
vị view. Spring cung cấp view resolver dùng để render mô hình trong
browser mà không cần buộc đến view cụ thể.
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
HandlerInterceptor: chặn các request đến, không được điều khiển bởi
DispatcherServlet
LocaleResolver: giải quyết, và tùy chọn lưu cục bộ của một người dùng
cá nhân
MultipartResolver: tạo điều kiện thuận lợi cho làm việc với các tập tin tải
lên bằng cách gói yêu cầu gửi đến
2.5 Hoạt động của Spring MVC
22
- Dispatcher Servlet nhận request
- Dispatcher Servlet sẽ dựa vào HandlerMapping và gọi Controller tương ứng với
request
- Controller thực thi và trả về đối tượng ModelAndView cho DispatcherServlet.
ModelAndView chứa Model và tên của View
- DispatcherServlet sẽ gửi tên của View cho View Resolver để tìm View thật sự.
- DispatcherServlet sẽ gửi Model cho View để render ra kết quả cuối cùng và trả về
cho user
3 JSTL (JSP Standard Tag Library)
Vào năm 1997 Sun Microsystems giới thiệu các API Servlet. Servlet đã tạo 1 sự
khác biệt rất lớn cho các nhà phát triển Web bằng CGI vì servlet mạnh mẽ và hiệu quả
hơn. Tuy nhiên khi đưa vào áp dụng, các nhà lập trình đã chỉ ra rằng servlet có một thiếu
sót hết sức quan trọng đó là các nhà phát triển web tạo ra giao diện về phía client bằng
cách xuất ra các đoạn code HTML thông qua câu lệnh print. Điều này gây khó khăn
trong việc duy trì mở rộng code. Điều thiếu sót này nhanh chóng được khắc phục
bằng JSP cho phép nhúng các đoạn code Java.
Tuy nhiên, như trong hầu hết các trường hợp, giải pháp này lại có một thiếu sót của
riêng nó: mã Java nhúng trong HTML sẽ dẫn đến sự phức tạp trong các trang JSP, sự kết
hợp code business logic và presentation làm cho trang JSP trở nên khó hiểu và khó duy
trì. Hơn nữa, việc chỉ giới hạn trong một số thẻ tiêu chuẩn như <jsp:useBean> hay
<jsp:include>, JSP có thể tốt cho những người phát triển Web thông hiểu Java, nhưng sẽ
gây khó khăn cho những người chưa có kinh nghiệm nhiều trong Java. Để đối phó với
khó khăn này, các nhà phát triển Java lợi dụng một ưu điểm của Java là cho phép tạo ra
các custom tag, rất nhiều custom tag được tạo ra ví dụ như Jakarta Taglibs được tạo ra bởi
Apache Software Foundation
23
Từ sự phát triển của Jakatar Taglibs và những phản ứng tiêu cực khi phát triển web
trên JSP, JavaServerPages Standard Tag Library (JSTL) được sinh ra. Sự kết hợp
giữa Expression language và thư viện tiêu chuẩn toàn diện, JSTL đã tận diệt sự cần
thiết phải dùng scriptlet và expression trong trang JSP
3.1 JSTL là gì
JSTL bao gồm các phần sau:
- Expression Language
- Thư viện các hành động tiêu chuẩn
- Thư viện cho validation
- Expression Language được cho là tính năng quan trọng nhất của JSTL.
Expression Language cho phép truy xuất vào các implicit object như request,
response, các biến liên quan đến scope
- Thư viện các hành động tiêu chuẩn cung cấp một nền tảng vững chắc cho việc
lập trình web từ việc lập vòng lập, import URLS đến kết nối database, đọc
XML….
3.2 Các thư viện thẻ của JSTL
- Thư viện thẻ của JSTL được chia làm 4 loại như sau:
Tên thư viện (prefix) Mô tả
Core (c) Chứa các hành động cơ bản như if, forEach….
Formatting (fmt) Định dạng và quốc tế hóa
SQL (sql) Truy xuất CSDL
XML (x) Phân tích và đọc dữ liệu từ XML
3.2.1 Core Action: gồm các chức năng sau
- Send output to the current JspWriter
- Gửi output đến JspWriter hiện tại
- Thao tác trên các thành phần của Java Bean
- Triển khai statement if hay switch
- Xử lý các Exception
- Duyệt qua các mảng giá trị, chuỗi
- Tạo các URLs và import tài nguyên
- Redirect servlet responses
- Chuyển các trả lời của Servlet
- Tạo và hủy các biến trong scope
- Các Action trong Core:
Tên Action Ý nghĩa
<c:catch> Catch những exception xảy ra trong code body
<c:choose> Chọn 1 trong nhiều đoạn mã để thi hành khi thỏa mãn điều kiện
nào đó
<c:forEach> Vòng lặp quét hết tập hợp nào đó
<c:forToken> Vòng lặp quét hết 1 chuỗi nào đó theo chuỗi phân cách
24
<c:if> Thi hành đoạn code khi thỏa mãn điều kiện
<c:import> Import một URL nào đó
<c:otherwise> Xác định giá trị mặc định cho <c:choose>
<c:out> Gửi output cho JspWriter hiện hành
<c:param> Xác định 1 tham số URL cho <c:import> hay <c:url>
<c:redirect> Điều hướng request sang URL mới
<c:remove> Xóa 1 đối tượng trong 1 scope nào đó
<c:set> Tạo 1 đối tượng trong 1 scope nào đó
<c:url> Tạo 1 URL
<c:when> Khai báo điều kiện trong <c:choose>
3.2.2 Formatting Action:
JSTL formatting Action cho phép người lập trình quốc tế hóa trang web của mình.
Người lập trình có thể:
- Xác định resource bundle cho các thông báo với nhiều ngôn ngữ
- Xác định khu vực để định dạng ngày tháng, tiền tệ, phần trăm
- Các Action trong Formatting
Tên Action Ý nghĩa
<fmt:bundle> Xác định resoure bundle cho các messge đi kèm trong thẻ
fmt:bundle
<fmt:setBundle> Xác định resoure bundle và gán vào 1 biến để dùng sau này
<fmt:formatDate> Định dạng ngày theo 1 vùng địa lý nào đó
<fmt:formatNumber> Định dạng số theo 1 vùng địa lý nào đó
<fmt:message> Truy xuất vào 1 message trong bundle
3.3 Nguyên tắc thiết kế:
Để tận dụng tốt nhất của JSTL, bạn nên tìm hiểu những nguyên tắc thiết kế của
JSTL, các nguyên tắc này áp dụng đối với nhiều thẻ trong JSTL nhưng không phải tất
cả. Các nguyên tắc thiết kế thảo luận trongphần này là:
- Qui ước đặt tên
- Phạm vi của biến
- Các thuộc tính động
- Xử lý lỗi
3.3.1 Qui ước đặt tên
- Tên thẻ và tên thuộc tính của thẻ được đặt tên theo qui ước đặt tên của Java: qui
tắc Lạc Đà, từ đầu tiên là chữ thường, các từ tiếp theo được viết hoa đầu từ. Ví dụ:
<c:out value='${param.amount}'/>
<c:forEach var='item' items='${names}' varStatus='status'>
3.3.2 Scoped Variables vs. Scripting Variables
25
- Các thẻ JSTL như là một ống dẫn giữa trang JSP và mã code của Java. Thông
qua ống dẫn này, thông tin được truyền từ trang JSP vào code Java và ngược lại.
Hãy xem xét ví dụ sau đây:
<c:forEach var='item' begin='1' end='10'>
value = <c:out value='${item}'/><br>
</c:forEach>
- Trong đoạn mã trên, trang JSP sử dụng thẻ <c:forEach> để gửi thông tin qua
giá trị của biến “var” cho class tag handler. Class tag handler tạo ra 1 đối tượng
“item” như là một biến cục bộ cho thẻ <c:forEach>
3.3.3 Các thuộc tính động
- Giá trị của các thuộc tính trong JSTL có thể tĩnh hoặc động. Giá trị của thuộc
tính động có thể được xác định thông qua các Expression Language (EL)
3.3.4 Xử lý lỗi
- JSTL làm tất cả để tránh xảy ra các lỗi trong quá trình thi hành. Trong nhiều
trường hợp nếu bạn khai báo giá trị null hay chuỗi rỗng cho thuộc tính, giá trị đó
sẽ được chuyển đổi thành các hằng số vô hại ( như “0” chẳng hạn)
3.4 Thẻ General-Purpose and Conditional
- Một số nhiệm vụ cơ bản của các nhà phát triển JSP bao gồm tạo ra output, truy
xuất và thay đổi giá trị các biến trên các scope, các thuộc tính của JavaBean, thực
các biểu thức điều kiện và xử lý exceptions
- Các thẻ General-purpose:
- JSTL cung cấp 4 thẻ sau cho General Purpose:
<c:out>
<c:set>
<c:remove>
<c:catch>
- Ví dụ: chúng ta làm một ví dụ đơn giản về các thẻ general purpose như sau: