Hướng dẫn cấu hình project Hibernate – JPA

Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu cách cấu hình một project sử dụng Hibermate kết hợp với JPA. Nếu bạn nào còn xa lạ với JPA thì đừng lo lắng nhé, nó chỉ là các interface cung cấp các giao thức chuẩn để làm việc với database. Chúng ta hoàn toàn có thể dùng Hibermate riêng lẽ mà ko cần đến JPA. Thế nhưng gần đây JPA trở nên khá phổ biến, điển hình như Spring framework đang sử dụng JPA và Hibermate là một JPA provider (Implement các JPA interface) mặc định.

Maven dependency

Hồi sinh viên mình cũng đã từng điên cuồng với cái mớ file jar cài đặt thủ công, nào là tải về, thêm vào eclipse etc. Bỏ cái mớ đó đi, chúng ta sẽ chuyển sang sử dụng maven một công cụ quản lý dự án mạnh mẽ chúng ta có thể download và quản lý các thư viện bên ngoài thông qua các denpendency được đặt trong file pom.xml.

Để sử dụng Hibermate trong maven chúng ta cần thêm các denpendency sau:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>hibermate-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.11</version>
        </dependency>
    </dependencies>
</project>

Module hibernate-entitymanager bao gồm các module khác mà chúng ta cần sử dụng trong Hibernate như hibermate-coreJava Persistence interface. 

Persistence Unit là gì?

Trước khi đi vào phần cấu hình một project Hibermate -JPA, chúng ta cần một hiểu khái niệm persistence-unit. Trong JPA, persistence.xml được dùng để cấu hình project, trong đó phải có tối thiểu một persistence-unit nơi định nghĩa tất cả các metadata mà EntityManagerFactory cần như entity mapping, thông tin kết nối cơ sở dữ liệu, transactions và các cấu hình khác của JPA provider cung cấp, ví dụ bạn sử dụng Hibernate là một JPA provider thì ngoài các thông tin nói trên thì chúng ta có thể thêm các cấu hình của Hibernate trong persistence.xml. 

Trong một file persistence.xml phải chứa tối thiểu một persistence-unit.

Vị trí đặt file cấu hình JPA persistence XML

Mặc định, persistence.xml được đặt trong thư mục META-INF nằm trong thư mục gốc của Java classpath. Nếu đang sử dụng project maven, chúng ta có thể đặt META-INF trong thư mục resources.

src/main/resources/META-INF/persistence.xml

Cấu trúc JPA persistence XML

Cấu trúc của một file persistence.xml sẽ giống như sau:

<persistence
        version="2.1"
        xmlns="http://xmlns.jcp.org/xml/ns/persistence"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence_2_1.xsd">
    <persistence-unit name="helloword" transaction-type="RESOURCE_LOCAL">
        <description>
            Hypersistence Optimizer is a dynamic analyzing tool that can scan
            your JPA and Hibernate application and provide you tips about the
            changes you need to make to entity mappings, configurations, queries,
            and Persistence Context actions to speed up your data access layer.
        </description>

        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <class>entities.Message</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.schema-generation.database.action"
                    value="drop-and-create"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name ="hibernate.show_sql" value = "true" />
            <property name="hibernate.use_sql_comments" value="true"/>
            <property name="org.hibernate.SQL" value="debug"/>
            <property name="org.hibernate.type.descriptor.sql.BasicBinder" value="debug"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/example?useSSL=false" />
            <property name="javax.persistence.jdbc.user" value="root" />
            <property name="javax.persistence.jdbc.password" value="123456" />
        </properties>
    </persistence-unit>
</persistence>

Trong đó phần tử gốc là persistence khai báo JPA version, xml-model(bản mẫu) dùng để kiểm tra các thuộc tính trong persistence.xml.

Persistence-unit

Thuộc tính persistence-unit được dùng để định nghĩa tên của một Persistence Unit (được giải thích ở phần trên) trong JPA. 

Thuộc tính transaction-type định nghĩa JPA transaction strategy hiểu đơn giản là các chiếm lược transaction sẽ xử lý trong các tình huống nhất định ví dụ như có một lệnh sql không thực thi được thì toàn bộ các câu lệnh thực thi trước đó sẽ bị rollback lại etc. Chúng ta có 2 giá trị 

  • JTA
  • RESOURCE_LOCAL

Thông thường trong các ứng dụng sử dụng Java EE sẽ dùng JTA mặc định cho phép đồng bộ hoá các thay đổi trên entity cho nhiều nguồn dữ liệu khác nhau như database systems, JMS queues, Caches), nhưng nó yêu cầu phải có JTA transaction manager sử dụng 2PC (Two-Phase Commit) protocol để áp dụng.

Nếu bạn chỉ dùng duy nhất cho một cơ sở dữ liệu quan hệ thì không cần thiết JTA, sử dụng RESOURCE_LOCAL trong trường hợp này, đây cũng là giá trị mà mình đặt trong bài hướng dẫn này.

description

Mô tả chi tiết hơn về mục đích sử dụng của persistence-unit.

provider

provider dùng để chỉ định một JPA provider triển khai tất cả các API của JPA, như trong bài viết này là Hibernate. Nếu bạn đang sử dụng Hibermate phiên bản 4.3 hoặc mới hơn thì  sử dụng org.hibernate.jpa.HibernatePersistenceProvider, còn nếu từ phiên bản 4.2 trở xuống thì dùng org.hibernate.ejb.HibernatePersistence.

Entity Mapping

Mặc định Hibernate tìm kiếm tất cả các entity mapping(các class ánh xạ từ Java object xuống table trong csdl) dựa trên @Entity annotation, chúng ta không cần khai báo các entity mapping class.

exclude-unlisted-classes

Nếu không muốn sử dụng cơ chế tự động tìm kiếm các entity mapping class, chúng ta có thể gán exclude-unlisted-classes thành true để tắt tính năng này.

class

Sau khi gán giá trị exclude-unlisted-classes = true, chúng ta cần chỉ định các entity mapping class thủ công thông qua thuộc tính class.

<class>entities.Message</class>

Hibernate properties 

Sau khi hoàn thành các cấu hình cơ bản của JPA, chúng ta sẽ đến với phần quan trọng, cấu hình trong hibernate. Trong đó chúng ta cần một số thông tin bắt buộc để có thể kết nối đến database.

Action

Cài đặt javax.persistence.schema-generation.database.action với giá trị drop-and-create cho phép JPA xoá và khởi tạo lại database tại mỗi lần khởi chạy ứng dụng, nó sẽ tạo tất cả các table trong database tương ứng với các entity mapping class chúng ta định nghĩa.

Lưu ý drop-and-create chỉ nên dùng trong môi trường local hoặc muốn thử nghiệm để tránh làm mất thời gian. Nhưng trong thực tế thì ứng dụng nên tắt tính năng này và tạo database cách thủ công bằng các câu lệnh SQL thông thường.

Datasource

Các thông tin dùng để kết nối đến database bao gồm url tên csdl, driver, username và password.

<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/example?useSSL=false" />
<property name="javax.persistence.jdbc.user" value="root" />
<property name="javax.persistence.jdbc.password" value="123456" />

Lưu ý các thông tin trên phải điều chỉnh sao cho tương ứng với môi trường trên máy của các bạn như user/pass mỗi máy mỗi khác phụ thuộc vào các bạn đặt trong quá trình cài đặt.

Logging

Chúng ta luôn luôn phải quản lý các câu lệnh SQL được generate bởi Hibernate để có thể quản lý được logic của mình. Đây cũng là một cách giúp debug và tối ưu performance hiệu quả khi làm việc với Hibermate.

<property name="hibernate.format_sql" value="true"/>
<property name ="hibernate.show_sql" value = "true" />
<property name="hibernate.use_sql_comments" value="true"/>
<property name="org.hibernate.SQL" value="debug"/>
<property name="org.hibernate.type.descriptor.sql.BasicBinder" value="debug"/>

JPA – Hibernate implementaion

Định nghĩa entity

Sau khi cấu hình xong persistence.xml, chúng ta sẽ tiến hành định nghĩa entity class tương ứng đã khai báo trong persistence.xml.

persistence.xml config

Định nghĩa Message class trong entities package

package entities;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Message {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String text;
    public Message() {
    }
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
}

Trong đó chúng ta cần chú ý các điểm sau:

  • @Entity annotation dùng để chỉ định một mapping class tương ứng với một bảng trong database.
  • @ID chỉ định các thuộc tính của khoá chính trong table Message. 
  • @GeneratedValue dùng để sinh giá trị tự động cho ID.

Thao tác dữ liệu với JPA – Hibermate

Một EntityManagerFactory tương ứng với một persistence-unit name đã được định nghĩa trong persistence.xml. Vì khởi tạo một EntityManagerFactory object sẽ hao tốn nhiều tài nguyên nên một ứng dụng chỉ nên tạo một EntityManagerFactory instance duy nhất và dùng chung cho tất cả những nơi cần thao tác với database.

Mỗi phiên làm việc với database chúng ta sẽ tạo ra một EntityManager instance từ EntityManagerFactory và hãy nhớ close nó khi hết phiên làm việc. Để bắt đầu một transaction chúng ta cần gọi lênh begin(), và đồng bộ xoá dữ liệu xuống database với commit(). Khi lệnh commit() được gọi, Hibermate sẽ sinh ra các câu lệnh SQL tương ứng và đẩy xuống database.

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import entities.*;

public class Main {

    public static void main(String[] agrs) {
        EntityManagerFactory emf =
                Persistence.createEntityManagerFactory("helloword");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();

        Message message = new Message();
        message.setText("Hello World!");
        em.persist(message);

        em.getTransaction().commit();
        em.close();
        emf.close();
    }
}

Output:

Hibernate: 
    drop table if exists Message
Hibernate: 
    create table Message (
        id bigint not null auto_increment,
        text varchar(255),
        primary key (id)
    )
Hibernate: 
    /* load entities.Message */ select
        message0_.id as id1_0_0_,
        message0_.text as text2_0_0_ 
    from
        Message message0_ 
    where
        message0_.id=?
Hibernate: 
    /* insert entities.Message
        */ insert 
        into
            Message
            (text) 
        values
            (?)

Sau khi thêm dữ liệu thành công vào database chúng ta có thể kiểm tra bằng cách truy vấn và cập nhật dữ liệu với merge method nếu cần thiết. Lưu ý rằng đoạn code dưới đây phải thực thi ngay sau khi thêm dữ liệu thành công vì khi chạy chương trình lại thì dữ liệu sẽ bị xóa sạch vì cấu hình avax.persistence.schema-generation.database.action = drop-and-create. Hoặc chúng ta có thể comment cấu hình này sau lần chạy đầu tiên để tắt tính năng này.

import entities.Message;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.List;

public class QueryExample {

    public static void main(String[] agrs) {
        EntityManagerFactory emf =
                Persistence.createEntityManagerFactory("helloword");
        EntityManager em = emf.createEntityManager();
        em.getTransaction().begin();

        List<Message> messages = em.createQuery("select m from Message m").getResultList();
        System.out.println("Size: " + messages.size());
        System.out.println("Text: " + messages.get(0).getText());
        messages.get(0).setText("Take me to your leader!");
        em.getTransaction().commit();
        em.close();
        emf.close();
    }
}

Output

Size: 1
Text: Hello World!

Tóm lược

Nắm được các khái niệm căn bản trong cấu hình của một dự án Hibernate là một điều rất quan trọng để chúng ta có thể hiểu và sử dụng các framework hiện đại Spring boot. Bản thân mình đã mắc phải khi sử dụng Spring boot trong một khoảng thời gian dài mà không hiểu rõ về Hibernate, chính điều này khiến mình không cách nào sửa được các bug liên quan đến mapping entity trong Spring boot.

Sau cùng, các bạn có thể tham khảo mã nguồn được mình công khai trên gitlab.

Nguồn tham khảo

https://thorben-janssen.com/jpa-persistence-xml/

https://vladmihalcea.com/jpa-persistence-xml/

https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/configuration-optional.html

0 0 votes
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x