Menu

Liferay 6.2: Thao tác với cơ sở dữ liệu

Trong các hướng dẫn trước đây, chúng ta đã làm quen với việc xây dựng các ứng dụng (portlet) đơn giản. Tuy nhiên, chúng chỉ là các ví dụ mà thôi, để cho gần hơn với các ứng dụng trong thực tế, mình viết post này trình bày về các thao tác với cơ sở dữ liệu. Liferay cung cấp một công cụ cực kỳ mạnh mẽ và hiệu quả đó là Servvice Builder. Công cụ này sẽ giúp bản triển khai một ứng dụng cực kỳ nhanh chóng.
Bài toán: Để đơn giản, chúng ta sẽ tạo ra một thực thể là Student gồm các thông tin như: mã sinh viên (code), họ tên đầy đủ (fullname), ngày sinh (birthtay), giới tính và địa chỉ. Ngoài ra, chúng ta còn có thuộc tính Id (studentId) là khóa chính của bảng Student trong cơ sở dữ liệu của chúng ta.
Các bước làm của chúng ta như sau:
  1. Tạo portlet có tên là Database-portlet.
  2. Tạo Service Builder.
  3. Định nghĩa thực thể Student trong Service Builder trên.
  4. Dịch Service Builder và xem xét các thành phần được sinh ra.
  5. Thực hiện các thao tác với bảng Student (select, insert).
Bắt đầu nhé!

Tạo portlet có tên là Database-portlet

Bước này hoàn toàn đơn giản, giống như hướng dẫn ở bài này. Trong portlet này, ta sẽ sử dụng để cập nhật và hiển thị các bản ghi của bảng Student lên màn hình.

Tạo Service Builder

Thực ra, thao tác này khá đơn giản bạn chỉ cần click chuột phải vào tên của project và chọn New >> Liferay >> Liferay Service Builder. Như hình dưới đây:
Tạo service builder
Bạn click vào nút Next và hoàn thành thông tin trong bảng sau:
Thông tin của service builder
Trong hình trên, ta thấy có 3 thông tin cần điều:
  1. Package path: chính là tên package mà sau này các model và service sẽ được tạo ra.
  2. Namespace: được sử dụng làm tiền tố cho tên của các bảng được sinh ra bởi service builder này.
  3. Author: tên tác giả (không cần thiết)
Bạn nhấn vào nút Finish và sẽ thấy có một têp tin có tên là service.xml được tạo ra. trong thư mục docroot/WEB-INF/service.xml.
Bây giờ, hãy mở file service.xml ra ta chuyển sang bước tiếp theo. Định nghĩa thực thể Student.

Định nghĩa thực thể Student

Liferay IDE cung cấp công cụ cho phép ta chỉnh sửa file Service Builder dưới 3 dạng gồm:
  • Overview: cho phép chỉnh sửa file service.xml dưới dạng giao diện đồ họa.
  • Source: cho phép chính sửa dưới dạng code.
  • Diagram: quan hệ giữa các bảng trong cơ sử dữ liệu (vì chỉ có một bảng thôi nên ta sẽ không dùng phần này khi nào làm việc với nhiều bảng mình sẽ giới thiệu sau.)
Ở đây, chúng ta sẽ chỉnh sử dưới dạng source nhé. Sử dụng giao diện cũng dễ dàng hơn dưới dạng code nhưng mình vẫn thích sử dụng dạng code hơn. Bạn mở file service.xml bằng cách click đúp vào nó và chuyển sang tab source nhé.
Nội dung mặc định của file service.xml sẽ như sau:

Ta quan sát từ dòng 1 đến dòng 6.
  • Dòng 1: khai báo phiên bản xml
  • Dòng 2: khai báo các file DTD mà chúng ta sử dụng. Chúng ta sử dụng file DTD liferay-service-builder_6_2_0.dtd (file này định nghĩa các cấu trúc và chúng ta sẽ đi giải thích từng cấu trúc ở các bài sau).
  • Dòng 3: thẻ service-builder là thẻ gốc để định nghĩa tất cả các thành phần khác.
  • Dòng 4: thuộc tính package-path khai báo đường dẫn đến package sẽ chưa các file được sinh ra sau khi build.
  • Dòng 5: thẻ author khai báo tên tác giả thôi
  • Dòng 6: thẻ namespace chứa chuỗi tiền tố.
  • Dòng 8-45: chứa thông tin thực thể Foo, bây giờ bạn xóa các dòng này đi và định nghĩa thực thể Student của mình nhé.
Ta sẽ viết lại file service.xml như sau:

Ta thấy, các dòng từ 1-6 là không thay đổi ta chỉ thay đổi phần thức thể. Bây giờ chúng ta sẽ đi xem xét các dòng từ 8-27 để biết ý nghĩa của chúng là gì nhé.

  • Dòng 8: thẻ entity cho phép ta định nghĩa một thực thể. Thuộc tính name chính ta tên model sẽ được sinh ra sau khi chúng ta dịch. Thuộc tính local-serviceremote-service sẽ có giá trị là true hoặc false. Từ cái tên của các service cho biết được ý nghĩa của chúng rồi. Ở đây mình cho 2 thuộc tính này có giá trị là true tức là cho phép được truy cập nội bộ hoặc truy cập từ các ứng dụng khác. Cuối cùng là thuộc tính table đây chính là tên bảng sẽ được sinh ra sau này. Tên thực thể và tên bảng trong cơ sở dữ liệu có thể khác nhau nhé.
  • Dòng 10, 13: là ghi chú thôi nhé.
  • Dòng 11: dòng này sẽ định nghĩa trường studentId trong class Student. Trường studentId có kiểu là long và là khóa chính (Primary Key) của bảng Student. Thuộc tính db-name cho biết trường studentId tương ứng với cột nào trong cơ sở dữ liệu. Ở đây trường studentId tương ứng với cột StudentId.
  • Dòng 15-19: định nghĩa các trường khác của class Student. Mỗi trường đều có các thông tin gồm tên trường, kiểu dữ liệu và tên cột tương ứng được ánh xạ vào trường trên.
  • Dòng 22-24: định nghĩa thứ tự mặc định. Thực ra, Service Builder sẽ dựa vào thông tin này để đánh index trong cơ sở dữ liệu. Như vậy, bạn cần quan tâm đến các thông tin này để tối ưu cơ sở dữ liệu của bạn.
Như vậy, ta đã định nghĩa xong một thực thể có tên là Student. Tiếp theo, ta biên dịch và xem kết quả khi biên dịch file service.xml nhé.

Dịch Service Builder

Việc dịch vô cùng đơn giản, bạn chỉ cần click chuột phải vào tên project. Chọn Liferay >> Build Service hoặc nhấn phím tắt Ctrl+Alt+V. Quá trình build có thể mất một vài phút cứ kiên nhẫn chờ đợi nhé.
Dịch thành công
Hình trên có được khi chúng ta dịch thành công Service Builder. Bây giờ, ta đi xem xét các thành phần được tạo ra nhé. Hãy mở package com.blogspot.chingovan.tutorial.database chúng ta sẽ có nội dung như hình dưới đây:
Các lớp được sinh tự động
Có rất nhiều lớp được sinh ra. Nhưng chúng ta chỉ quan tâm đến lớp nào mà chúng ta thực sự sử dụng mà thôi. Ở đây, ta sẽ quan tâm đến các lớp: StudentLocalServiceImpl, StudentServiceImplStudentImpl mà thôi. Còn các lớp khác và vai trò của nó thì bạn đọc kỹ hơn trong hướng dẫn của Liferay nhé.
Bây giờ bạn deploy và kéo portlet Database-portlet vào page của mình nhé. Lúc này, bạn có thấy portlet này có gì đặc biệt không? Chẳng có gì đặc biệt của đùng không? Điều đặc biệt nằm ở chỗ khác cơ. Hãy mở cơ sở dữ liệu mà bạn tạo khi chạy Liferay lên đi. Có thêm 1 bảng nữa được tạo ra đúng không? Đó chính là bảng Student và bảng này có các trường giống hệt như mô tả trong file service.xm:
Một bảng mới được thêm vào cơ sở dữ liệu
Bạn mở bảng dữ liệu ra và thêm một vài bản ghi vào nhé. (Ở đây, mình dùng CSDL Postgresql nên tên của bảng và của các cột viết thường, đồng thời một số kiểu dữ liệu cũng hơi khác một chút. Anyway, chúng ta không quan tâm nhiều đến cơ sở dữ liệu chỉ cần biết lấy dữ liệu và lưu dữ liệu là đủ rồi).
Bây giờ ta sẽ lấy danh sách các bản ghi có trong cơ sở dữ liệu và hiển thị nên màn hình nhé. (Bạn nên đọc lại post Liferay: Tìm kiếm và liệt kê (Search Container)).

Thực hiện các thao tác với cơ sở dữ liệu

Bạn hãy mở file view.jsp trong thư mục html/database lên nhé. Nội dung file view.jsp sẽ như sau:
Bạn có thể xem file init.jsp tại đây.
Bây giờ deploy portlet và refresh màn hình ta sẽ thấy nội dung trên màn hình như sau:
Danh sách sinh viên trên màn hình
Như các bạn thấy, ta đã lấy được danh sách sinh viên từ cơ sở dữ liệu và hiển thị trên màn hình. Như vậy, ở dòng số 4 của file view.jsp cho phép ta lấy danh sách tất cả các sinh viên. Phương thức StudentLocalServiceUtil.getStudents cho phép ta lấy các bản ghi từ cơ sở dữ liệu lên với hai chỉ số start và end (từ bản ghi start đến bản ghi end). Ở đây mình dùng giá trị QueryUtil.ALL_POS (-1) để lấy tất cả các bản ghi.
Bây giờ, ta sẽ sửa file view.jsp để mở giao diện edit.jsp cho phép ta thêm mới một sinh viên vào cơ sở dữ liệu. Nội dung sau khi sửa sẽ như sau:
Bây giờ ta viết file edit.jsp có nội dung như sau:
Trong file này, ta tạo thẻ portlet:actionURL để với thuộc tính name="addStudent" để xử lý khi ta nhấn nút submit. Từ dòng 6-14 là nội dung của form với trường tương ứng với các thuộc tình của Student. Thẻ <aui:input> sẽ tự động tạo ra thẻ input và các thẻ liên quan (ta sẽ bàn ở bài khác). Ở đây có một số kiểu như text, checkbox, date (tùy thuộc vào từng trình duyệt nhé (Chrome hỗ trợ nhiều loại hơn). Bây giờ ta deploy nên xem nội dung như thế nào nhé. Bạn click vào link Click to add a Student để mở giao diện edit.
Giao diện chỉnh sửa
Trong hình là giao diện chỉnh sửa. Để ghi những dữ liệu này vào cơ sở dữ liệu ta cần phải xử lý trong file Portlet. Nội dung của file portlet sẽ như sau:

Ta thêm phương thức addStudent để lấy dữ liệu từ giao diện và ghi vào file. Chúng ta sử dụng tiện ích ParamUtil để lấy các dữ liệu tương ứng. Sau đó tạo ra một đối tượng Student ở dòng 30. Dòng 31-35 ghi những dữ liệu lấy được từ màn hình ghi vào trong đối tượng. Dòng 37 sẽ ghi xuống database.
Bây giờ chúng ta deploy portlet và điền dữ liệu và nhấn vào nút Save. Kết quả sẽ như hình dưới đây:
Sau khi thêm một Student
Trong hình, ta thấy một sinh viên mới được thêm vào. Bạn có thể xem trong cơ sở dữ liệu, có một bản ghi mới đã được thêm vào.

Kết luận

Vậy là trong bài này ta đã biết cách sử dụng Service Builder để tạo ra các entity và service tương ứng. Tuy nhiên, đây chỉ là mức sơ xài mà thôi. Trong các ứng thực tế có rất nhiều thực thể các quan hệ phức tạp khác sẽ nói ở các post sau. Ngoài ra, bài này mời chỉ thực hiện hai thao tác là select và insert, các thao tác khác sẽ viết chi tiết ở post khác. Đáng lẽ ra mình sẽ kết hợp giữa post Tìm kiếm và liệt kê (Search Container)để hiển thị dữ liệu đẹp hơn.

13 nhận xét:

  1. sao làm giống bạn mà cứ báo lỗi hoài thế ta . Bạn có thể chỉ mình lỗi này là gì không ?
    1. ERROR in C:\liferay\liferay-plugins-sdk-6.2-ce-ga2-20140319114139101\liferay-plugins-sdk-6.2\portlets\lala-portlet\docroot\WEB-INF\service\com\entity\model\Foo.java (at line 1)
    [javac] /**
    [javac] ^
    [javac] The type java.util.Map$Entry cannot be resolved. It is indirectly referenced from required .class files
    [javac] ----------
    [javac] ----------
    [javac] 2. ERROR in C:\liferay\liferay-plugins-sdk-6.2-ce-ga2-20140319114139101\liferay-plugins-sdk-6.2\portlets\lala-portlet\docroot\WEB-INF\service\com\entity\service\FooLocalServiceClp.java (at line 336)
    [javac] ClpSerializer.translateInput(orderByComparator)
    [javac] ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    [javac] The type java.util.Comparator cannot be resolved. It is indirectly referenced from required .class files
    [javac] ----------
    [javac] 2 problems (2 errors)

    Trả lờiXóa
    Trả lời
    1. Trước tiên, cảm ơn bạn đã quan tâm đến blog của mình.
      Bạn thử tải ví dụ của mình xem thế nào đã nhé: https://github.com/programmerit/liferay-tutorial/tree/master/combine-portlet
      Theo mình thấy, thì lỗi của bạn do chưa biên build thành công file service.xml nên không tạo ra các POJO cần thiết.

      Xóa
    2. thanks bạn. Mình hiểu lỗi là do mình để java mặc định là 1.8

      Xóa
  2. link file init bị lỗi mong thầy có thể sửa lại link mới

    Trả lờiXóa
    Trả lời
    1. Cảm ơn Ngô Huy,
      Do mình cài lại máy tính lên không backup đầy đủ các dữ liệu trong thư mục git nên trong quá trình đồng bộ nó tự động xóa mấy project (Database-portlet). Mình đã cập nhật lại file init.jsp trong (Gist) rồi, thực tế, file này chỉ để khai báo tên các gói, các lớp sẽ sử dụng mà thôi. Để không phải khai báo nhiều ở các file khác, ta khai báo chúng trong một file và các file jsp khác thì chỉ cần include vào là có.

      Xóa
  3. List students = StudentLocalServiceUtil.getStudents(QueryUtil.ALL_POS, QueryUtil.ALL_POS);
    minh bi loi o day la sao vay ban...chi giao dum

    Trả lờiXóa
    Trả lời
    1. Chào bạn,
      Cảm ơn câu hỏi của bạn. Mình xin trả lời bạn như sau: Khi bạn tạo các Entity trong service.xml (ServiceBuilder) và build thì sẽ tự động tạo ra các Lớp và các phương thức tiện ích như: thêm (add), sửa (update), truy vấn (getXXX). Như trong hướng dẫn của mình thì có 1 thực thể Student, chính vì vậy khi build sẽ được các lớp tiện ích như StudentLocalService, StudentLocalServiceUtil và ở đây phương thức getStudents là được sinh ra tự động.
      Bạn bị lỗi ở phương thứ getStudents thì mình đoán là do bạn chưa build service. Để build bạn click chuột phải vào project >> Chọn Liferay >> Chọn Build Services.
      Bạn đợi 1 chút khi nào chương trình thông báo thành công (BUILD SUCCESSFULL) ở màn hình console thì thành công. Lúc này, bạn Refresh lại project và deploy vào cổng của mình là được.
      Chúc bạn thành công và có bất cứ thắc mặc nào mình sẵn sàng giúp đỡ!

      Xóa
  4. Chào bạn!
    Mình đang bắt đầu tìm hiểu về liferay, qua bài viết của bạn mình vẫn chưa hiểu thông qua service buider có thể connect đến database như thế nào và khi tạo entity tại file service.xml và build thì cơ chế sẽ thực hiện như thế nào ?

    Trả lờiXóa
    Trả lời
    1. Chào bạn,
      Mình xin trả lời câu hỏi của bạn như sau:
      1. Connect đến database như thế nào: Service Builder sẽ sử dụng kết nối mặc định của cổng (xem ở bài này: http://chingovan.blogspot.com/2015/05/cai-dat-va-cau-hinh-liferay.html). Khi bạn deploy portlet vào portal, hệ thống sẽ tự xác định các bảng, index được thêm vào, xóa đi hay sửa đổi thì sẽ thực hiện thao tác tương ứng ở database. Ví dụ trong bài này, sau khi deploy bạn sẽ thấy có 1 bảng mới có tên là Student được thêm vào trong cơ sở dữ liệu với các trường như bạn đã khai báo trong service.xml.
      2. service.xml được sử dụng để khai báo. Khi build, hệ thống sẽ tạo ra các model, service, persistence tùy thuộc vào khai báo trong file service.xml của chúng ta. Các lớp này sẽ được sinh tự động, chính vì vậy ta sẽ không mất nhiều thời gian ở các lớp dưới.
      Service Builder là một công cụ hữu ích, làm quen với Liferay bạn lên sử dụng được nó.
      Cảm ơn đã ghé thăm blog của mình!

      Xóa
    2. Thank bạn!
      Bạn cho mình hỏi chút. nếu mình thêm ,sửa , xóa colum ở bảng mới được tạo ra mà mình lại k có file service.xml thì mình sẽ phải chỉnh những file nào trong chỗ module được tạo ra đó?

      Xóa
    3. Chào bạn,
      Mình không hiểu rõ là nếu không sử dụng file service.xml (Service Builder) thì bạn sinh ra các bảng (quan hệ) như thế nào? Nếu bạn sử dụng Hibernate, thì cần sửa ở file mapping, model và database (xóa bằng lệnh thôi), hoặc có thể bạn thêm lệnh xóa vào trong file tables.sql ở thư mục docroot/WEB-INF/sql.
      Bạn nên làm chi tiết hơn câu hỏi hơn, điều này sẽ giúp mình có thể trả lời chính xác hơn.
      Cảm ơn!

      Xóa
  5. các thao tác khác bạn viết trong post nào vậy

    Trả lờiXóa
    Trả lời
    1. Chào bạn, về cơ bản các thao tác CRUD của service builder khá đơn gian và cũng đã được hỗ trợ hết nên mình không viết tiếp.
      Bạn có thể xem thêm nội dung này: https://chingovan.blogspot.com/2015/07/liferay-ket-hop-search-container-va-co.html

      Xóa