Thứ Tư, 10 tháng 2, 2021

Bài 8: Các kỹ thuật hỗ trợ chương trình trong Spring 5

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

Bài 8: Các kỹ thuật hỗ trợ chương trình

Mã Hoàng Nhật Phi, Trương Trần Tiến

 

-         Mục đích: Ở bài viết trước chúng ta đã thực hiện thành công chức năng CRUD đơn giản cùng shopping cart online sử dụng Framework Spring 5. Bài viết này sẽ cung cấp thêm cho chúng ta những kỹ thuật giúp cho chương trình của chúng ta linh động hơn trong việc maintain, thay đổi yêu cầu. Chúng ta sẽ thực hiện thay đổi và sử dụng nhiều hơn một Datasource trong chương trình của các database khác nhau. Bên cạnh đó, bài viết cũng sẽ cung cấp việc mapping 2 object có các tên field khác nhau sử dụng mapstruct kết hợp môt số các tips khác để cải thiện chương trình

-         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        Đã hoàn tất các bài xây dựng chức năng liên quan đến Spring MVC5 tại địa chỉ http://www.kieutrongkhanh.net/search/label/Spring5

-          Tool sử dụng:

o   Netbeans 8.x

o   JDK 8 update 66

o   DBMS: SQL Server từ 2005 đến 2019, PostgreSQL 9 hay 10

 

I.        Giới thiệu

-         Ở bài viết trước, chúng ta đã thực hiện thành công chức năng CRUD kết hợp với chức năng mua hàng online – shopping cart sử dụng Framework Spring 5. Bài viết này sẽ cung cấp thêm cho chúng ta những kỹ thuật giúp cho chương trình của chúng ta linh động hơn trong việc maintain, thay đổi yêu cầu. Chúng ta sẽ thực hiện thay đổi và sử dụng nhiều hơn một Datasource trong chương trình của các database khác nhau. Bên cạnh đó, bài viết cũng sẽ cung cấp việc mapping 2 object có các tên field khác nhau sử dụng mapstruct kết hợp môt số các tips khác để cải thiện chương trình.

II.      Nội dung

1.      Thay đổi datasource cho ứng dụng

-          Do chúng ta sử dụng JPA với provider là ORM Hibernate nên chúng ta đã tách được sự phụ thuộc giữa database và source code Java. Chúng ta có thể dễ dàng thay đổi hoặc thêm thông tin datasource bằng việc cấu hình mà không cần phải thay đổi tên bảng, tên cột trong các câu sql trong source code như dùng JDBC.

-          Trong bài này, chúng ta sử dụng PostgreSQL 9 hay 10 (tải DBMS tại PostgreSQL: Downloads, việc thao tác và cài đặt PostgreSQL quí vị có thể tham khảo bất kỳ website nào - ở đây chúng tôi recommend quí vị trang hướng dẫn Cài đặt cơ sở dữ liệu PostgreSQL trên Windows (openplanning.net)) với mục tiêu thể hiện việc thay đổi bất kỳ database nào cũng sẽ không ảnh hưởng đến việc implementation ứng dụng. Các bước thực hiện như sau

                                    a.            Tạo mới database ở PostgreSQL

-         Chúng ta sử dụng database tương tự với cấu trúc như khi sử dụng Microsoft SQL Server ở bài 2 (http://www.kieutrongkhanh.net/2020/08/bai-2-cau-hinh-ket-noi-csdl-su-dung.html)

-         Lưu ý: Chúng ta cần lưu ý các kiểu dữ liệu trên các cột trong Postgre sẽ khác với kiểu dữ liệu trong Microsoft SQL Server khi tạo cấu trúc bảng

                                    b.            Add PostgreSQL dependency vào pom.xml (thư mục Project File)

-         Chúng ta thêm hai dependency là driver cho Postgre và dependency để dùng cho naming ở database (map từ camel case sang snake case do trong Postgre nếu ta có table name là TblRegistration thì postgre sẽ ánh xạ thành tbl_registration).

-         Run maven goal “clean install” để tải dependency

                                       c.            Cập nhật thay đổi tại WEB-INF/application-config.xml

Text, application

Description automatically generated

-         Các nội dung cập nhật trong các khung tô đỏ với mục tiêu là thay đổi cấu hình từ DBMS SQL Server trong các bài trước trỏ đến PostgreSQL. Cụ thể việc thay đổi cấu hình của Datasource và EntityManagerFactory

-         Trong trường hợp, quí vị dùng PostgreSQL version 9 thì chỉ cần thay giá trí trong khung tô đỏ dòng 35 thành org.hibernate.dialect.PostgreSQL9Dialect

                                       d.            Thực hiện clean install, deploy và chạy thử để kiểm tra kết quả

-         Kiểm tra câu lệnh query để xác thực đổi database

Graphical user interface, text, application, email

Description automatically generated

-         Như vậy, chúng ta đã thay đổi cơ sở dữ liệu mà không cần thay đổi tới Java source code. Đây là sự ưu việc cho việc thay đổi cấu hình thông qua file XML mà không làm thay đổi ứng dụng

2.      Thêm datasource cho ứng dụng

-         Ở phần trước chúng ta đã biết cách thay đổi hoàn toàn datasource cho ứng dụng, nhưng trong vài trường hợp ứng dụng của chúng ta cần truy cập nhiều hơn 1 database. May mắn Spring cho phép chúng ta cấu hình nhiều hơn 1 Datasource và EntityManagerFactory

-         Ở phần trước, chúng ta thay đổi MSSQL thành PostgreSQL, phần này chúng ta sẽ thêm lại MSSQL.

                                       a.            Thêm cấu hình Datasource mới tại WEB.INF/application-config.xml

Graphical user interface, text, application, email

Description automatically generated

-         Chúng ta thay đổi tên bean datasource của PostgreSQL và tạo một bean Datasource mới của MSSQL.

                                       b.            Thêm và thay đổi cấu hình của EntityManagerFactory

Graphical user interface, text, application, email

Description automatically generated

Graphical user interface, text, application

Description automatically generated

-         Chúng ta thay đổi id của bean entityManagerFactory thành PostgreSQLEntityManagerFactory để phân biệt với bean MSSQLEntityManagerFactory mà chúng ta đã thêm mới

-         Đồng thời chúng ta thay đổi “ref” của property “dataSource” để tương ứng với 2 dataSource bên trên

                                       c.            Thay đổi config của bean transactionManager

Graphical user interface, application, Word

Description automatically generated

-         Chúng ta thêm bean MSSQLTransactionManager, thay đổi id cho bean transactionManager thành PostgreSQLTransactionManager và “ref” đúng EntityManagerFactory tương ứng

                                    d.            Thay đổi config của tx:annotation-driven và jpa:repositories

Graphical user interface, application

Description automatically generated

-         Chúng ta thêm tag <tx:annotation-driven> để có thể sử dụng annotation @Transactional với các TransactionManage tương ứng

-         Đồng thời, chúng ta thêm tag <jpa:repositories>, trong 2 tag này chúng ta cần chỉ định rõ ràng bean EntityManagerFactory tương ứng.

-         Lưu ý: vì trong bài viết chỉ có 1 repository nên base-package của 2 tag <jpa:repositories> là trùng nhau, khi đó Spring sẽ chọn cái xuất hiện sau cùng làm database cho các Inteface repository trong package đó. Trong thực tế, khi sử dụng 2 database khác nhau, chúng ta cần phải đặt 2 interface ở 2 package khác nhau

o   Khi sử dụng @Transactional, chúng ta cần chỉ định rõ ràng transactionManager và @Transaction được import ở package “org.springframework.transaction.annotation.Transactional

Graphical user interface, text, application, email

Description automatically generated

                                    e.            Chạy goal Tomcat và kiểm tra 2 trường hợp

-         Trường hợp 1: đặt <jpa:repositories> của PostgreSQL ở dưới và kiểm tra câu lệnh query

A picture containing text

Description automatically generated

Graphical user interface, text, application, email

Description automatically generated

-         Trường hợp 2: đặt <jpa:repositories> của MSSQL ở dưới và kiểm tra câu lệnh query

A picture containing text

Description automatically generated

3.      Dùng mapstruct để map các Object có field khác tên

-          Giả sử, chúng ta cần thay đổi response model trả về cho client hiển thị, chúng ta không muốn trả field password về cho client và đổi tên field admin trở thành role cho client. Nếu chúng ta sử dụng entity để giao tiếp với client thì khi sửa đổi như vậy, chúng ta phải thực hiện thay đổi rất nhiều code trong project.

-          Nhưng do chúng ta đã sử dụng mapstruct để deep copy dữ liệu ở các tầng nên việc thay đổi sẽ không ảnh hưởng đến nhiều thành phần trong ứng dụng.

                                    a.            Tạo class NewRegistrationResponeModel mới trong package responsemodel

-         Chúng ta tạo class NewRegistrationResponseModel tương ứng với các attribute trả về cho phía client: username, lastname, role. Class này có cấu trúc như là 1 Java Bean.

                                    b.            Tạo một interface mapper mới để map từ DTO sang NewRegistrationResponseModel

Graphical user interface, text, application, email

Description automatically generated

-          Chúng ta thêm một interface NewResponseModelMapper cũng extends từ ResponseModelGenericMapper nhưng ở đây ta khai báo lại phương thức toResponseModel. Chúng ta dùng annotation @Mapping để map dữ liệu từ object này sang object khác trong trường hợp không cùng tên. Khi chúng ta có nhiều @Mapping, chúng ta sẽ đặt trong một mảng của annotation @Mappings với value là một mảng các @Mapping.

-          Giá trị source sẽ là tên field nằm trong class RegistrationDTOtarget là tên field sẽ được mapping ở class NewRegistrationResponseModel.

-          Đồng thời nếu các giá trị nào không tồn tại ở NewRegistrationResponseModel, mapstruct sẽ không mapping

Graphical user interface, text, application

Description automatically generated

                                    c.            Inject bean mapper và thay đổi method search ở RegistrationRestController

Graphical user interface, text, application

Description automatically generated

Graphical user interface, text, application

Description automatically generated

-         Chúng ta tiến hành inject bean Mapper mới vào trong RegistrationRestController và thay đổi method của @GetMapping(“/search-user”)

-         Khi ấy cấu trúc của JSON trả về phía người dùng như sau:

                                    d.            Cập nhật main.js

Text

Description automatically generated

A picture containing application

Description automatically generated

-         Chúng ta cập nhật main.js, comment các dòng có liên quan đến tạo các element password trong hàm renderHeader và renderRow

-         Đồng thời,  chúng ta phải thay đổi cách render checkbox role, lấy attribute role từ chuỗi JSON mới update ở phần trên.

-         Lưu ý: chúng ta chỉ mới update ResponseModel cho phần Search user, còn chức năng update cần có các RequestModel và các method Javascript cũng cần phải chỉnh sửa. Vì ở đây, trọng tâm chính của bài viết là dùng Mapstruct để custom mapping object nên sẽ không đi sâu vào việc sửa đổi chức năng Update (phần này quí vị có thể tự chỉnh sửa để tăng tính trải nghiệm của chính quí vị về tính flexible khi sử dụng framework)

                                    e.            Chạy maven goal để clean install, deploy và chạy để kiểm tra lại kết quả

4.      Các tips để cải thiện chương trình

                                    a.            Sử dụng các annotation @GetMapping, @PostMapping.

-         Khi sử dụng trong Controller, chúng ta sử dụng annotation @RequestMapping và đưa vào đó các giá trị của URL đồng thời là Http method

-         VD: @RequestMapping(value = {"/", "/login"}, method = RequestMethod.GET), @RequestMapping(value = "/login", method = RequestMethod.POST)

-         Chúng ta có thể thay thế chúng thành @GetMapping(value = {"/", "/login"}), @PostMapping("/login")

Graphical user interface, text, application, email

Description automatically generated

                                       b.            Cách để Redirect sang 1 trang

-         Hiện tại, khi có Request tới một URL chúng ta trực tiếp sử dụng ModelAndView để trả về lại cho phía client. Spring cung cấp cho chúng ta 2 cách thức để có thể redirect.

o   Sử dụng class RedirectView

o   Sử dụng prefix: redirect:/

-         Tại method logout trong MainController, khi logout thành công chúng ta trả về login.jsp nhưng trên đường dẫn vẫn là /logout

Graphical user interface, application

Description automatically generated

Graphical user interface, application

Description automatically generated

-         Chúng ta tiến hành giữ nguyên class trả về ModelAndView và sử dụng prefix “redirect:/” và xem lại kết quả.

Graphical user interface, text, application, email

Description automatically generated

Graphical user interface, application

Description automatically generated

Graphical user interface, application

Description automatically generated

-         Chúng ta thay thể class ModelAndView thành RediretView và kiểm tra lại kết quả.

Graphical user interface, text, application

Description automatically generated

Graphical user interface, application

Description automatically generated

Graphical user interface, application

Description automatically generated

                                    c.            Sử dụng HTTP header và check session trong method search ở Main Controller

-         Hiện tại, nếu chúng ta logout và back lại trang search thì chúng ta vẫn thấy được kết quả, đồng thời nếu client truy cập trực tiếp đường dẫn /search thì không cần đăng nhập vẫn có thể truy cập được.

o   Chúng ta cần sử dụng Header Cache-Control với giá trị no-cache, no-store để báo cho browser biết sẽ không lưu lại trang search

o   Check attribute “USER” trong session nếu không sẽ trả lại trang login.jsp

Graphical user interface, text, application, email

Description automatically generated

-         Trong trường hợp này chúng ta sẽ sử dụng cách tạm trên để bảo mật trang search hơn một tí. Để có thể bảo mật một cách đầy đủ thì Spring Framework cung cấp cho chúng ta module Spring Security và Spring Session. Các module này sẽ được giới thiệu trong các bài viết sau.

 

Chúc mừng quí vị, chúng ta vừa tìm hiểu xong các kỹ thuật để có thể giúp chương trình của chúng ta trở nên dễ dàng sửa đổi trong quá trình maintain code hoặc có sự thay đổi ở yêu cầu.

 

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ị ở bài viết sau trong loạt Spring MVC 5.

 

 

 

1 nhận xét:

  1. Mình gặp phải bug khi run tomcat đó là không tạo được bean "entityManagerFactory". Vì "key=hibernate.dialect" không nhận giá trị "value=org.hibernate.dialect.PostgreSQL10Dialect" nữa!!
    Truy cập vào link này: "https://docs.jboss.org/hibernate/orm/5.2/javadocs/index.html?org/hibernate/boot/model/naming/PhysicalNamingStrategyStandardImpl.html" để lấy Hibernate API phù hợp cho việc configure nhé.

    Trả lờiXóa