Thứ Hai, 23 tháng 11, 2020

Bài 4: Thực hiện việc update dữ liệu và bắt ràng buộc về dữ liệu khi thực hiện chức năng update

Xây dựng ứng dụng CRUD với Spring 5

Bài 4: Thực hiện việc update dữ liệu và bắt ràng buộc về dữ liệu khi thực hiện chức năng update

Mã Hoàng Nhật Phi

Trương Trần Tiến Editor

 

-         Mục đích: Tiếp nối series bài viết về xây dựng ứng dụng web dùng Spring 5 Framework, bài viết trước chúng ta đã trải nghiệm việc truy vấn dữ liệu từ DB sử dụng Spring Framework cộng thêm việc trình bày dữ liệu trên giao diện client sử dụng XMLHTTPRequest để gọi RESTful Webservices nhằm chuyển đổi dữ liệu từ object thành dạng JSON để dùng JavaScript trình bày ở client. Trong bài viết này, chúng ta sẽ thực hiện chức năng update và thực hiện kiểm tra ràng buộc để thông báo lỗi cho người dùng đồng thời cập nhật lại giao diện lưới trình bày sau khi thực hiện chức năng update xong (refresh hay gọi lại chức năng khác một lần nữa).

-         Yêu cầu kiến thức cơ bản:

o        Nắm vững và sử dụng các kỹ năng để về việc xây dựng hoàn tất ứng dụng với mô hình MVC2

o        Nắm vững các khái niệm về ngôn ngữ lập trình Java, lập trình thao tác hướng đối tượng.

o        Nắm vững các khái niệm về lập trình web sử dụng J2EE hay JavaEE với các kiến thức về Servlet, JSP. http://www.kieutrongkhanh.net/search/label/Servlet%26JSP

o        Tìm hiểu và nắm vững các khái niệm và ứng dụng XMLHttpRequest trong việc gọi ứng dụng hạn chế postback ở client (behind the scene) tại địa chỉ https://www.w3schools.com/xml/xml_http.asp http://www.kieutrongkhanh.net/2016/12/du-lieu-uoc-request-tu-server-va-chuyen.html

o        Tìm hiểu và nắm được các khái niệm cùng với các kỹ năng sử dụng RESTful Web Services, tham khảo tại địa chỉ http://www.kieutrongkhanh.net/2016/08/xay-dung-ung-dung-ap-dung-restful-web.html  hay http://www.kieutrongkhanh.net/2016/08/gioi-thieu-ve-restful-web-services-cong.html

o        Đã hoàn tất bài xây dựng chức năng Search sử dụng Spring MVC5 tại địa chỉ http://www.kieutrongkhanh.net/2020/08/bai-3-thuc-hien-viec-truy-van-va-trinh.html

-         Tool sử dụng:

o   Netbeans 8.x

o   JDK 8 update 66

o   DBMS: SQL Server từ 2005 đến 2019, PostgreSQL 10 (Đón xem nội dung bổ sung trong loạt bài viết tiếp theo)

                                           I.                        Giới thiệu

-         Sau bài viết số 3, chúng ta đã nắm cơ bản về cách truy vấn dữ liệu và cung cấp thành API dưới dạng web services để từ đó có thể consume một cách độc lập ở server sử dụng format trung gian (JSON) mà không cần render dữ liệu ở phía server (giảm performance và không phù hợp với việc client có thể sử dụng bất cứ giao diện gì từ browser đến mobile hay bất kỳ thiết bị trình bày khác có khả năng trình bày). Tiếp theo, chúng ta thực hiện việc bắt ràng buộc dữ liệu trước khi đồng bộ vào DB thông qua chức năng update

                                        II.                        Nội dung

1.      Tạo UpdateUserRequest

-          Tương tự cách suy nghĩ như tính năng Search, chúng ta sẽ phải xây dựng Response Model để mapping dữ liệu từ request vào DTO để xử lý với tên là UpdateUserModelMapper được đặt tại package modelmapper.

-         Đầu tiên công việc cần làm với chức năng update là xác định ràng buộc khi dữ liệu được đưa vào để cập nhật phải tương thích với dữ liệu và ràng buộc dưới DB.

-         Chúng ta cần định nghĩa các ràng buộc về dữ liệu cần bắt trong quá trình nhập liệu của người dùng. Việc bắt ràng buộc này được diễn ra trong khi request đang xảy ra.

o   Chúng ta xây dựng class UpdateUserRequest trong package request để xử lý các ràng buộc về dữ liệu, cụ thể là password từ 8 đến 30 ký tự; lastname từ 2 đến 50 ký tự

 

o   Lưu ý, các tên property tương ứng phải được mapping tên tương ứng với DTOentity Registration (xem tham khảo lại các case study trong bài số 3 để có thể phát hiện lỗi cần chỉnh sửa)

o   Tuy nhiên, khi gõ code chúng ta sẽ phát sinh lỗi tại dòng @Length

o   Lỗi này phát sinh là do chưa import thư viện hỗ trợ ràng buộc khi check dữ liệu. Chúng ta cần bổ sung thư viện validation với maven vào tập tin pom như sau

o   Chúng ta thực hiện bổ sung property hibernate.validator versiondependency cho hibernate validator

o   Thực hiện lệnh clean – install thì code sẽ hỗ trợ visual để chúng ta import để khắc phục lỗi

2.      Tạo Mapper cho UpdateUserRequest

-          Tiếp theo, chúng ta implement interface UpdateUserModelMapper với thành phần dữ liệu mapping DTO với dữ liệu được đưa vào UpdateUserRequest sau khi kiểm tra tính toàn vẹn

3.      Tạo class CommonResponse

-          Bởi vì việc xử lý update là có xử lý liên quan đến việc bắt lỗi nhập liệu của người dùng cũng như lỗi phát sinh trong quá trình cập nhật dữ liệu, cho nên chúng ta cần xây dựng instance chứa các property cần thiết để có thể trả về cấu trúc message chung cho toàn ứng dụng.

-          Common Response là đối tượng được implement thành class để thực hiện điều này. Chúng ta cài đặt class tại CommonResponse tại package response và chứa các property xác định kết quả việc xử lý (success), thông báo (message) và lỗi phát sinh (error)

 

-         Việc chúng ta xây dựng method build với static bởi vì bản chất Spring không sử dụng cơ chế stateful (singleton) do vậy việc sử dụng static sẽ làm cho performance tăng lên trong quá trình triệu hồi thực thi. Đây là một giải pháp kết hợp factory method để cập nhật thay đổi đối tượng đang có sẵn.

4.      Bổ sung method update vào bên trong service

-         Trong method update, chúng ta đã thực hiện kiểm tra xem ở dưới database đã có dữ liệu tương ứng hay chưa nếu có thì thực hiện update, ngược lại thì throw EntityNotFoundException.

-         Chúng ta cần lưu ý đến method findById đã được có sẵn trong JpaRepository với tham số là kiểu dữ liệu của khóa chính (ở Spring 4 thì hàm trên sẽ trả về chính entity) nhưng từ Spring 5 entity sẽ được wrap lại trong một Optional (kiểu dữ liệu có từ java 8).

-         Chúng ta cũng có thể dùng hàm findByUsername đã định nghĩa ở phần login. Tuy nhiên, để chúng ta cùng nghiên cứu các tình huống khác nhau trong sử dụng chúng ta sử dụng kiểu dữ liệu của Java 8 sẳn có để minh họa cho việc bắt exception tại ExceptionHandlerController.

-         Sau khi thực hiện kiểm tra được hành động update đã được thực hiện, chúng ta thực hiện gọi method saveAndFlush để đưa dữ liệu xuống database ngay mà không cần phải chờ commit của transaction do spring quản lý như method save.

-         Lưu ý: ở đây chúng ta có khả năng phát sinh lỗi khi viết code tại dòng 57 như sau

o   Lỗi này phát sinh là cách sử dụng Java 8 phải được build với maven 1.8, do vậy cho dù chúng ta cấu hình property là 1.8 nhưng nếu plugin build của chúng ta điền 1.7 (trong file pom.xml) thì lỗi phát sinh như trên

o   Chúng ta thực hiện đổi source và target trong plugin thành 1.8 thì code sẽ hết lỗi

5.      Bổ sung chức năng update trong Rest Controller

o   Đầu tiên chúng ta khai báo bổ sung property cho Rest Controller để sử dụng injection với IoC cho UpdateUserModelMapper

private final UpdateUserModelMapper updateUserModelMapper;

o   Cập nhật thêm tham số cho contructstor để đảm bảo injection được Spring hỗ trợ thực hiện

o   Cài đặt method update như code bên dưới

o   Một số giá trị thiết lập trong code chúng ta cần hiểu rõ được thể hiện bên dưới

o   @RequestBody annotation được dùng để mapping Http Request Message body thành một object mà chúng ta định nghĩa trước, trong trường hợp này là class UpdateUserRequest.

§  Spring sẽ tự động mapping request thông qua tập hợp các converter class có sẵn như “MappingJackson2HttpMessageConverter” (convert thành JSON), “Jaxb2RootElementHttpMessageConverter” (convert thành XML),….

§  Spring sử dụng “Content-type” header để có thể lựa chọn các Converter class cho phù hợp.

§  Tham khảo chi tiết về nội dung này tại đây https://www.javadevjournal.com/spring/spring-http-message-converter/

o   @Valid annotation được dùng để thông báo cho Spring pass object đến validator. Nếu như validation không thành công “MethodArgumentNotInvalidException” sẽ được throw.

o   Khi put message được gửi đến từ phía client, Spring sẽ convert thông tin trong chuỗi JSON thành UpdateUserRequest object và pass object này vào validator.

o   Nếu validation thành công, chúng ta mapping sang DTO và cập nhật thông tin xuống database.

o   Nếu cập nhật thành công chúng ta sẽ gửi 200 response message về phía client.

6.    Tạo ExceptionHandlerController

-         Tới đây, trong chương trình của chúng ta sẽ ném Exception trong 2 trường hợp:

o   Khi entity cần update không có dữ liệu trong database

o   và, khi thông tin update không hợp lệ.

-         Chúng ta xử lý lỗi nếu có bằng cách đón nhận ExceptionHandlerController sử dụng annotation @ControllerAdvice. Đây cũng là một controller nhưng được gọi xử lý khi có exception ném ra ở các class Controller khác.

-        

-          Khi chúng ta sử dụng @ControllerAdvice để bắt Exception mà không bắt ngay trong Controller, điều này cho phép chúng ta chuyên biệt chức năng của Controller.

o   Rest Controller chỉ quan tâm đến việc tiếp nhận thông tin và update trong database.

o   Chức năng catch exception sẽ được ExceptionHandlerController quản lý. Điều này cho phép chương trình của ta phân tách rõ ràng (low coupling – high cohesive).

o   Đây là quy tắc chính trong khái niệm lập trình AOP.

-          Aspect Orient Programing (AOP) là một kỹ thuật lập trình với mục đính tăng tính riêng biệt của các component.

o   Khi hoạt động, chương trình sẽ kết hợp các component lại với nhau nhưng khi cần sửa chữa chúng ta chỉ cần thay đổi 1 component.

-          Trong một hệ thống sẽ có 2 mối quan tâm (concern):

o   Core concern / Primary concern: đây là chức năng chính của chương trình, là logic xử lý chính

o   Cross-cutting concern (Advice): đây là những chức năng phụ dùng để đảm bảo chất lượng của chương trình như logging, security, handle exception, ….

-          Trong application của chúng ta thì @RestController hoặc @Controller chính là Primary concern@ControllerAdvice chính là cross-cutting concern.

o   Khi chạy, Spring sẽ kết hợp các Controller này lại với nhau giúp đảm bảo được tính ổn định của chương trình.

-          Ngoài @ControllerAdvice trong SpringMVC, Spring Framework còn có cả một module Spring AOP chuyên biệt.

o   Tham khảo thêm chi tiết nội dung đã nêu tại https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html

 

-         Chúng ta thực hiện tạo ExceptionHandlerController class tại package controller

o   Sau khi tạo class và extends, chúng ta gõ chữ handleMethod và nhấn tổ hợp phím control – space bar để phát sinh method override cho chúng ta.

o   Class ResponseEntityExceptionHandler là một base class được Spring cung cấp cho các @ControllerAdvice controller.

§  Class này sẽ cung cấp những method xử lý các exception thường gặp trong Spring MVC như sau:

·        HttpRequestMethodNotSupportedException

·        TypeMissmatchException

·        MethodArgumentNotValid ….

o   Trong trường hợp của bài viết, chúng ta cần phải override lại method handleMethodArgumentNotValid bởi vì đây là exception được throw trong quá trình validate như đề cập ở trên.          

o   Lưu ý: vì ResponseEntityExceptionHandler chỉ là một class do Spring cung cấp chúng ta có thể extends hoặc không tùy ý. Nếu không extends code sẽ thay đổi như sau đây

Graphical user interface, text, application, email

Description automatically generated

o   Trong code thực hiện nêu trên, chúng ta cần hiểu rõ thêm một số nội dung như sau

§  @ExceptionHandler giúp Spring xác định đây là method bắt các exception được throw, cách hoạt động tương tự như @GetMapping trong Rest Controller.

§  Cụ thể ở method “handleEntityNotFound” sẽ được trigger khi “EntityNotFoundException” bị ném trong chức năng Update ở Rest Controller.

§  Chúng ta sử dụng class trả về ở các method là ResponseEntity .Class này cho phép linh động xây dựng HttpResponse Message như add header, thay đổi status code, add message body.

7.    Thay đổi main.js – Cập nhật giao diện

-          Sau khi thực hiện thao tác, chúng ta thực hiện cập nhật giao diện trình bày ở client

-         Chúng ta thực hiện rendering lại dữ liệu thông qua hàm renderRegistrationRows để cập nhật thêm thành phần liên quan đến Update

-          Tương tự, chúng ta thực hiện update lại hàm renderRegistrationRows để cập nhật thêm thành phần liên quan đến Update

-         Chúng ta thực hiện bổ sung chức năng Update user vào file main.js. function updateUser thực hiện cơ bản nội dung như sau

o   Lấy các giá trị của các control trong table trình bày kết quả search được thể hiện qua các class name được chúng ta định nghĩa

-         Chúng ta thực hiện bổ sung file css vào trong chương trình. Tạo thư mục css trong thư mục resouces -> tạo file main.css. Đồng thời cập nhật search.jsp.

Diagram

Description automatically generated

Graphical user interface, application, Word

Description automatically generated

-         Chúng ta thực hiện bổ sung reference của nội dung css vừa tạo vào trang search.jsp như code theo sau

 

Graphical user interface, text, application, email

Description automatically generated

-          Chúng ta thực hiện lệnh clean – install. Sau đó, deploy để test chức năng update

o   Chúng ta thực hiện đến chức năng search

o   Nếu chúng ta thực hiện cập nhật sai như bỏ trống cột password và lastname, kết quả chúng ta sẽ nhận được

Graphical user interface, table

Description automatically generated

o   Nếu chúng ta thực hiện cập nhật kết quả đúng, chúng ta nhận được kết quả như sau

 

Lưu ý: Với trình duyệt IE 11, nếu data trên giao diện quay lại giá trị trước khi update nhưng data ở database đã thay đổi thì chúng ta thực hiện việc xử lý cache khi sử dụng XHR trên IE 11 như sau

o   Ở một số máy, trình duyệt IE 11 cache lại và trả về kết quả khi cùng url và cùng param giống nhau, điều này không xảy ra trên các trình duyệt khác.

o   Khi chức năng update thực hiện thành công, chúng ta sẽ gọi lại hàm search nhưng IE nhận ra url và param truyền đi tương tự đã truyền nên trả về kết quả đã được cache trên browser. Cho dù, chúng ta dễ dàng nhận thấy tính năng không hoạt động sai dù database đã được cập nhật. Bên cạnh đó, chức năng vẫn chạy đúng trên các trình duyệt khác ngoài IE

o   Để khắc phục vấn đề cache của IE như đã nêu trên, chúng ta thêm một tham số là timestamp là giờ hiện tại với mục tiêu để các param được hình thành trong các request sẽ khác nhau.

o   Chúng ta điều chỉnh sửa main.js như sau:

Chúc mừng quí vị, chúng ta vừa hoàn tất việc bắt ràng buộc về dữ liệu và thông báo lỗi cho người sử dụng thông qua chức năng update. Hy vọng nội dung này sẽ hỗ trợ quí vị trong việc sử dụng Spring Framework để xây dựng ứng dụng

 

Rất mong quí vị góp ý về nội dung loạt bài viết này. Hẹn gặp lại quí vị ở các bài viết sau về các nội dung liên quan đến việc thêm xóa (CD) sử dụng Spring 5 Framework.

 

 

 

1 nhận xét: