Sử dụng Framework Hibernate trong Java Web Application

hibernate.png

Hibernate Framework

Framework trong phần mềm là một khái niệm dùng để chỉ những “cấu trúc dùng để hỗ trợ đã được định nghĩa sẵn” mà trong đó những dự án phần mềm khác có thể sử dung nó để phát triển. Một framework bao gồm những program hỗ trợ, core library và một ngôn ngữ lập trình để giúp phát triển và gắn những thành phần khác nhau ứng dụng phần mềm lại với nhau. Hibernate là một trong những ORM Framework. Hibernate framework là một framework cho persistence layer. Như vậy sử dụng Hibernate framework giúp bạn phát triển ứng dụng nhanh và chỉ còn chú tâm vào những layer khác mà không cần chú tâm nhiều đến persistence layer nữa. Hibernate giúp lưu trữ và truy vấn dữ liệu quan hệ mạnh mẽ và nhanh. Hibernate cho phép bạn truy vẫn dữ liệu bằng ngôn ngữ SQL mở rộng của Hibernate (HQL) hoặc bằng SQL thuần.

Trong bài viết này tôi sẽ hướng dẫn các bạn làm quen với framework Hibernate qua những thao tác đơn giản. Hibernate cung cấp công cụ object relational mapping ORM như đã nói ở trên nên trong bài viết này tôi sẽ sử dụng luôn thế mạnh của framework này kết hợp cùng "JSF managed bean" và "JSF 2.x pages" để hiển lấy về dữ liệu và hiển thị ra giao diện web application.

Công cụ và môi trường phát triển

Trong bài viết này tôi sử dụng môi trường window 8.1 và cài đặt những IDE hỗ trợ sau:

Các bạn có thể dùng các hệ quản trị cơ sử dữ liệu khác như Oracle DB, Sql Server ... IDE để soạn thảo code thì có thể dùng những lựa chọn khác như eclip, IntelliJ IDEA ... mỗi editor tool đều có sự khác biệt một chút nhưng đa phần đều support Hibernate.

Tạo mới một database

Trong bài viết này tối sẽ sử dụng luôn IDE netbean để tạo mới và thao tác với database. Để tạo một database các vào tab Services click chuột phải vào connection và chọn Create Datatbase... 00.png

ở đây tôi lấy tên database là liem_report_db các bạn có thể thay đổi tùy theo ý thích 01.PNG

Bây giờ chúng ta sẽ tạo ra một table có với tên là User ở đây là table demo nên tôi chỉ tạo ít field, các bạn có thể tùy biến để phù hợp với yêu cầu công việc

02.PNG

như vậy là chúng ta đã có table User nhưng chưa có dữ liệu nên chúng ta sẽ thêm môt ít dữ liệu demo. Để thực hiện câu lệnh query luôn trên IDE thì chúng ta click chuột phải vào connect và chọn Execute Command...

03.PNG

Test dữ liệu vừa thêm vào

Select * from User;

Và chúng ta có kết quả:

04.PNG

Lưu ý: những bước ở trên mục đích là để tạo một database và thiết lập dữ liệu trong đó để sử dụng sau này, nếu bạn đã có database rồi thì không cần phải thực hiện loại. Tên database, tables, fields có thể tùy biến theo ý thích.

Tạo mới một project Web Application

Để tạo mới một project trong Netbean khá dễ bạn chỉ cần thao tác theo những bước sau:

  1. Trên thanh Menu bạn chọn File > New Project (sử dung phím tắt Ctrl-Shift-N). Ở phần Category bạn chọn loại ứng dụng muốn tạo là Web Application sau đó click vào nút Next.

05.PNG

06.PNG

Ở bước này tôi sẽ yêu cầu chọn Server để Run project ở đây tôi chọn luôn server Glass Server 4.1.1 đi kèm sẵn trong bản Netbean. Các bạn có thể chọn server khác.

06.PNG

Đến bước này khá quan trọng, các bạn chọn JavaServer FacesHibernate 4.3.1 có thể version sẽ khác đi, tùy thuộc vào version của bản framework bạn bạn tích hợp vào Netbean

08.PNG

Sau đó click vào button Finish. Thế là chúng ta đã tạo ra một project được import các library của Hibernate và JSF (JavaServer Faces)

Hệ thống sẽ tự động sinh ra cho chúng ta một file có tên hibernate.cfg.xml nằm trong Source Packages > <default package> giờ chúng ta sẽ thêm một số config cho nó như sau:

<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/liem_report_db</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password"/>
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.current_session_context_class">thread</property>
    <property name="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory</property>

nếu các bạn để ý thì đây là những property cung cấp thông tin connection, việc này sẽ giúp cho Hibernate có thể thao tác với database của chúng ta. Thẻ cuối cùng hơi đặc biệt một chút

<property name="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory</property>

ở đây chúng ta khai báo class sẽ xử lý genarate ra câu lệnh sql. Trong bản Hibernate 4 tôi sử dụng đường dẫn package như vây, nếu bạn sử dụng bản khác (thấp hơn) thì thay đổi đường dẫn tương ứng

09.PNG

Tạo mới một class HibernateUtil.java

Để tạo mới một Hibernate Util class chúng ta thao tác như sau:

  1. Chuột phải vào Source Packages chọn New > Other để mở cửa sổ New File wizard.
  2. Chọn Hibernate từ list danh mục và ở khung đối diện File Types ta chọn HibernateUtil.java. Sau đó click vào button Next. Type HibernateUtil for the class name and dvdrental for the package. Click Finish.

10.PNG

Nhập vào tên cho class bạn muốn tạo. Ở đây tôi đặt tên là HibernateUtil cho dễ nhớ và chọn Packageapp bạn có thể tùy biến tên class và package.

11.PNG

Tiếp theo chúng ta sẽ tạo một file Hibernate Reverse Engineering mục đích là để phục vụ cho việc mapping dữ liệu

12.PNG

Sau đó click vào button Next và chúng ta thấy hiện ra table có tên user như hình dưới:

13.PNG

Đây chính là tên table tôi tạo lúc đầu trong database, nếu các bạn tạo table khác thì tên nó sẽ hiện ra trong đó. Chúng ta chọn table và nhấn vào button Add move sang khung Selected Table sau đó chúng ta click button Finish

Hibernate Mapping Files and POJOs

Bây giờ chúng ta sẽ sử dụng Hibernate Mapping Files and POJOs để generate file từ table user đã chọn ở trước đó. Chúng ta thực hiện như sau:

  1. Chuột phải vào Source Packages chọn New > Other để mở New File wizard.
  2. Sau đó chúng ta chọn Hibernate ở khung bên cạnh chọn Hibernate Mapping Files and POJOs from Database sau đó click vào button Next.
  3. Trong dropdown list chọn file cấu hình hibernate.cfg.xmlhibernate.reveng.xml
  4. Check vào JDK 5 Language Features
  5. Check vào Domain CodeHibernate XML Mappings để hệ thống nhận biết định dạng cần thiết để generate code.
  6. Đặt tên cho Package name tôi chọn là app. Sau đó click vào button Finish.

Ở công đoạn này nhiều người thường hay gặp phải message thông báo lỗi như hình dưới:

14.png

Nguyên nhân là do hệ thống không tìm được class ClassicQueryTranslatorFactory mà bạn đã cung cấp ở file cấu hình của Hibernate. Hãy kiểm tra lại version của hibernate và chỉnh sửa lại package cho đúng ở thuộc tính

<property name="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory</property>

Lưu ý: để nhanh chóng check xem đường dẫn đã đúng hay chưa bạn chỉ cần tạo mới một Class và import đường dẫn đó vào, nếu không tồn tại IDE sẽ báo đỏ, còn không thì đường dẫn là hợp lệ

15.PNG

Sau đó ta click vào Finish. Hệ thống sẽ tự động sinh ra 2 file mới trong app package đó là User.javaUser.hbm.xml

16.PNG

Class User đã có sẵn các field và được mapping thành các property dễ dàng cho việc thao tác sau này. User.hbm.xml chứa thông tin mapping với user table: File: User.java

import java.util.Date;

/**
 * User generated by hbm2java
 */
public class User  implements java.io.Serializable {

     private int id;
     private String firstName;
     private String lastName;
     private String username;
     private String password;
     private Date created;

    public User() {
    }

    public User(int id, String username) {
        this.id = id;
        this.username = username;
    }
    public User(int id, String firstName, String lastName, String username, String password, Date created) {
       this.id = id;
       this.firstName = firstName;
       this.lastName = lastName;
       this.username = username;
       this.password = password;
       this.created = created;
    }

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }
    public String getFirstName() {
        return this.firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
    public String getLastName() {
        return this.lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getUsername() {
        return this.username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return this.password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    public Date getCreated() {
        return this.created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }
}

File: User.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Generated Jan 27, 2016 9:46:18 AM by Hibernate Tools 4.3.1 -->
<hibernate-mapping>
    <class name="app.User" table="user" catalog="liem_report_db" optimistic-lock="version">
        <id name="id" type="int">
            <column name="id" />
            <generator class="assigned" />
        </id>
        <property name="firstName" type="string">
            <column name="first_name" length="50" />
        </property>
        <property name="lastName" type="string">
            <column name="last_name" length="50" />
        </property>
        <property name="username" type="string">
            <column name="username" length="20" not-null="true" unique="true" />
        </property>
        <property name="password" type="string">
            <column name="password" length="32" />
        </property>
        <property name="created" type="time">
            <column name="created" length="8" />
        </property>
    </class>
</hibernate-mapping>
Test query với HQL Query

Hibernate hỗ trợ thực thi HQL query để phục vụ cho việc truy xuất dữ liệu trong quá trình phát triển, bước này để test xem việc config, dữ liệu để connect tới db, mapping data đã thành công hay chưa: Chuột phải vào file hibernate.cfg.xmlchọn Run HQL Query để mở cửa sổ HQL Query sau đó ta thực hiện câu lệnh truy vấn

from User

Và được kết quả như trong ảnh, nếu không hiện ra hoặc báo Generate erro thì bạn kiểm tra lại file cấu hình của hibernate

17.PNG

Tạo mới class UserHelper.java

Chúng ta tạo chuột phải vào app package để create một class tên có thể tùy biến, ở đây tôi đặt là UserHelper cho dễ nhớ:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package app;

/**
 *
 * @author DUYLIEMPRO
 */
public class UserHelper {

    public UserHelper() {
    }

}

Sau đó viết thêm các method để lấy dữ liệu ra:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package app;

import java.util.ArrayList;
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;

/**
 *
 * @author DUYLIEMPRO
 */
public class UserHelper {

    Session session = null;
    List<User> userList;

    public UserHelper() {
        this.session = HibernateUtil.getSessionFactory().getCurrentSession();
    }

    @SuppressWarnings("unchecked")
    public List<User> getUserList() {
        userList = new ArrayList<User>();
        try {
            org.hibernate.Transaction tx = session.beginTransaction();
            Query q = session.createQuery("from User as user");
            userList = (List<User>) q.list();
        } catch (Exception e) {
            userList = null;
            e.printStackTrace();
        }
        return userList;
    }

    public User getUserByID(int userId) {
        User user = null;
        try {
            org.hibernate.Transaction tx = session.beginTransaction();
            Query q = session.createQuery("from User as user where user.id=" + userId);
            user = (User) q.uniqueResult();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return user;
    }
}

Tạo mới JSF Managed Bean

Bước này mục đích là tạo ra bean class để lấy dữ liệu fill ra file view. Để tạo một bean class các bạn thực hiện như sau:

  1. Chuột phải vào source package chọn New > Other.
  2. Chọn JSF Managed Bean trong danh mục JavaServer Faces. Sau đó click Next.
  3. Nhập tên bean class UserController.
  4. Nhập tên package là app
  5. Nhập UserController cho ô name sau này sẽ được sử dụng gọi ở file view (managed bean) 6 Thiết lập ScopeSession. Sau đó click vào Finish.

File: UserController.java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package app;

import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
import java.io.Serializable;

/**
 *
 * @author DUYLIEMPRO
 */
@Named(value = "userController")
@SessionScoped
public class UserController implements Serializable {

    /**
     * Creates a new instance of UserController
     */
    public UserController() {
    }

}

Chúng ta sẽ viết các method xử lý lấy dữ liệu vào class này, ở đây tôi sẽ viết ví dụ 2 method, các bạn có thể thêm tùy ý:

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package app;

import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
import java.io.Serializable;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;

/**
 *
 * @author DUYLIEMPRO
 */
@Named(value = "userController")
@SessionScoped
public class UserController implements Serializable {

    DataModel useList;
    UserHelper helper;
    private User current;

    /**
     * Creates a new instance of UserController
     */
    public UserController() {
        helper = new UserHelper();
    }

    public User getSelected() {
        if (current == null) {
            current = new User();
        }
        return current;
    }

    public DataModel getUseList() {
        if (useList == null) {
            useList = new ListDataModel(helper.getUserList());
        }
        return useList;
    }
}

Lưu ý

Muốn sử dụng được những function này trong file view (.xhtml) thì bắt buộc chúng ta phải viết tên method theo quy ước "get" + tên method(Tên method phải viết hoa chữ cái đầu). Khi hiển thị trên file view thì tiền tố "get" tự động sẽ bị remove, chữ cái đầu trong tên method sẽ tự động chuyển sang thường.

Tạo mới file Web Pages

Ở đây tôi sẽ sử dụng template để dễ dàng hơn cho việc thiết lập layout chung cho nhiều page, để tạo một template các bạn làm như sau:

  1. Chuột phải vào source package chọn New > Other.
  2. Chọn Facelets Template trong danh mục JavaServer Faces. sau đó click Next.
  3. Tôi đặt tên là template và chọn layout style sử dụng CSS (Table đều được).
  4. Click vào button Finish. Kết quả là chúng ta được một file: template.xhtml trong thư mục web pages
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html">

    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <h:outputStylesheet name="./css/default.css"/>
        <h:outputStylesheet name="./css/cssLayout.css"/>
        <title>Facelets Template</title>
    </h:head>

    <h:body>

        <div id="top" class="top">
            <ui:insert name="top">Top</ui:insert>
        </div>

        <div id="content" class="center_content">
            <ui:insert name="content">Content</ui:insert>
        </div>

    </h:body>
</html>

Tùy layout bạn chọn mà sẽ có nội dung khác nhau nhưng tất cả đều thẻ

<ui:insert name="content">Content</ui:insert>

Đây là nơi mà nội dung sẽ được chèn vào khi sử dụng template này.

Khi tạo mới ứng dụng hệ thống sẽ tự động tạo mới cho ta file index.xhtml trong thư mục Web Pages chúng ta sẽ sử dụng luôn file này để hiển thị dữ liệu. Tôi sẽ sử dụng JSF lib để sử dụng layout và hiển thị dữ liệu ra:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://xmlns.jcp.org/jsf/core">
    <ui:composition template="./template.xhtml">
        <ui:define name="content">
            <h:form>
                <h:dataTable value="#{userController.useList}" var="item" border="0" cellpadding="2" cellspacing="0" rowClasses="jsfcrud_odd_row,jsfcrud_even_row" rules="all" style="border:solid 1px">
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="id"/>
                        </f:facet>
                        <h:outputText value="#{item.id}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="first_name"/>
                        </f:facet>
                        <h:outputText value="#{item.first_name}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="last_name"/>
                        </f:facet>
                        <h:outputText value="#{item.last_name}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="username"/>
                        </f:facet>
                        <h:outputText value="#{item.username}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="password"/>
                        </f:facet>
                        <h:outputText value="#{item.password}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value="created"/>
                        </f:facet>
                        <h:outputText value="#{item.created}"/>
                    </h:column>
                    <h:column>
                        <f:facet name="header">
                            <h:outputText value=" "/>
                        </f:facet>
                    </h:column>
                </h:dataTable>
                <br/>
            </h:form>
        </ui:define>
    </ui:composition>
</html>

Lưu ý

  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://xmlns.jcp.org/jsf/core"

Đây là khai báo tên các thẻ sẽ được sẽ được sử dụng trong file view . Các bạn có thể thay đổi tên thẻ sẽ sử dụng vd: xmlns:a="http://java.sun.com/jsf/html" và lúc dùng gọi <a:form>

Run Project

  1. Để chạy thử project các bạn chuột phải vào tên project chọn Clean and Build để hệ thống compile nếu có lỗi sẽ thông báo ở cửa sổ Output dưới. 2. Sau khi build xong chúng ta chuột phải vào projec một lần nữa chọn run (Lần đầu hệ thống sẽ start GlassFish Server nên hơi lâu một chút) nếu ok dưới cửa sổ sẽ thông báo
Incrementally deploying report
Completed incremental distribution of report
run-deploy:
Browsing: http://localhost:8080/report
run-display-browser:
run:
BUILD SUCCESSFUL (total time: 0 seconds)

và trình duyệt sẽ bật ra cửa sổ vối đường link: http://localhost:8080/report

Và chúng ta được kết quả như hình bên dưới: 18.PNG

Database file

Đây là file datbase export cho những bạn nào cần:

CREATE DATABASE `liem_report_db`;
USE `liem_report_db`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL,
  `first_name` varchar(50) DEFAULT NULL,
  `last_name` varchar(50) DEFAULT NULL,
  `username` varchar(20) NOT NULL,
  `password` varchar(32) DEFAULT NULL,
  `created` time DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `user` VALUES ('1', 'Tran', 'Liem', 'tranducliem', '25f9e794323b453885f5181f1b624d0b', '00:00:00');
INSERT INTO `user` VALUES ('2', 'Le', 'Minh', 'levanminh', '25f9e794323b453885f5181f1b624d0b', '00:00:00');
INSERT INTO `user` VALUES ('3', 'Phan', 'Tuan', 'phanquoctuan', '25f9e794323b453885f5181f1b624d0b', '00:00:00');
INSERT INTO `user` VALUES ('4', 'Tran', 'Sang', 'tranvansang', '25f9e794323b453885f5181f1b624d0b', '00:00:00');

Bài viết có tham khảo tài liệu User Guide của [Hibernate Framework](http://hibernate.org/orm/documentation/4.2/).

Xin cảm ơn các bạn rất nhiều ! LiemTD - Framgia.