báo cáo tìm hiểu Spring MVC
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: [...]... constructor bằng XML: ... 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... . "quizMaster" > < ref local = "springQuizMaster" /> </ property > </ bean > </ beans 2.3 Giới thiệu về Spring MVC Spring& apos;s Web MVC framework được thiết kế xung. mẫu Velocity. 2.4 Một số thành phần quan trọng trong Spring MVC 16 2.4.1 DispatcherServlet Spring& apos;s web MVC framework giống như nhiều web MVC frameworks, request- driven, được thiết kế xung. 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