Tra cứu Java Hibernate

Xem thêm các chuyên mục:

1- Giới thiệu

Đây là một tài liệu tra cứu một số vấn đề liên quan tới Hibernate. Để có thể dễ dàng tham khảo, bạn nên xem trước tài liệu:

2- Download các thư viện điều khiển một vài loại Database

Khi bạn làm việc một loại cơ sở dữ liệu nào đó, bạn cần có thư viện điều khiển loại cơ sở dữ liệu này.
  • Oracle
  • MySQL
  • SQLServer
  • HSQL
  • ....
Xem thêm:
Kết quả chúng ta có:

3- Cấu hình Hibernate trên các loại DB khác nhau

3.1- Cấu hình Hibernate cho Oracle.

Trước hết bạn phải khai báo thư viện điều khiển cơ sở dữ liệu Oracle (Hướng dẫn ở trên).
Cấu hình Hibernate:
hibernate.cfg.xml (Oracle)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
                     "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
                      "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property>  
<property name="connection.url">jdbc:oracle:thin:@localhost:1521:db11g</property>    
<property name="connection.username">simplehr</property>
<property name="connection.password">1234</property>


<!-- JDBC connection pool (use the built-in) -->
<property name="connection.pool_size">2</property>

<!-- SQL dialect -->
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>

<!-- Enable Hibernate's automatic session context management -->
<property name="current_session_context_class">thread</property>


<!-- Disable the second-level cache -->
<property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

<!-- Echo all executed SQL to stdout -->
<property name="show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">create-drop</property>


 <mapping class="org.o7planning.tutorial.hibernate.entities.Department" />
 <mapping class="org.o7planning.tutorial.hibernate.entities.Employee" />
 <mapping class="org.o7planning.tutorial.hibernate.entities.SalaryGrade" />
 <mapping class="org.o7planning.tutorial.hibernate.entities.Timekeeper" />

</session-factory>
</hibernate-configuration>

3.2- Cấu hình Hibernate cho MySQL

Trước hết bạn phải khai báo thư viện điều khiển cơ sở dữ liệu MySQL (Hướng dẫn ở trên).
Cấu hình Hibernate
hibernate.cfg.xml (MySQL)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
 <!-- Database connection settings -->
 <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
 <property name="connection.url">jdbc:mysql://tran-vmware:3306/simplehr</property>
 <property name="connection.username">root</property>
 <property name="connection.password">1234</property>

 <!-- JDBC connection pool (use the built-in) -->
 <property name="connection.pool_size">1</property>

 <!-- SQL dialect -->
 <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

 <!-- Enable Hibernate's automatic session context management -->
 <property name="current_session_context_class">thread</property>

 <!-- Disable the second-level cache  -->
 <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

 <!-- Echo all executed SQL to stdout -->
 <property name="show_sql">true</property>

  <mapping class="org.o7planning.tutorial.hibernate.entities.Department" />
  <mapping class="org.o7planning.tutorial.hibernate.entities.Employee" />
  <mapping class="org.o7planning.tutorial.hibernate.entities.SalaryGrade" />
  <mapping class="org.o7planning.tutorial.hibernate.entities.Timekeeper" />

</session-factory>
</hibernate-configuration>

3.3- Cấu hình Hibernate cho SQL Server

Trước hết bạn phải khai báo thư viện điều khiển cơ sở dữ liệu SQLServer (Hướng dẫn ở trên).
 
Cấu hình Hibernate (Sử dụng thư viện JTDS)
hibernate.cfg.xml (SQL Server)
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
     "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
     "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
  <!-- Database connection settings -->
  <property name="connection.driver_class">net.sourceforge.jtds.jdbc.Driver</property>
  <property name="connection.url">jdbc:jtds:sqlserver://localhost:1433/simplehr;instance=SQLEXPRESS</property>
  <property name="connection.username">sa</property>
  <property name="connection.password">1234</property>

  <!-- JDBC connection pool (use the built-in) -->
  <property name="connection.pool_size">1</property>

  <!-- SQL dialect -->
  <property name="dialect">org.hibernate.dialect.SQLServerDialect</property>

  <!-- Enable Hibernate's automatic session context management -->
  <property name="current_session_context_class">thread</property>

  <!-- Disable the second-level cache  -->
  <property name="cache.provider_class">org.hibernate.cache.internal.NoCacheProvider</property>

  <!-- Echo all executed SQL to stdout -->
  <property name="show_sql">true</property>

   <mapping class="org.o7planning.tutorial.hibernate.entities.Department" />
   <mapping class="org.o7planning.tutorial.hibernate.entities.Employee" />
   <mapping class="org.o7planning.tutorial.hibernate.entities.SalaryGrade" />
   <mapping class="org.o7planning.tutorial.hibernate.entities.Timekeeper" />

</session-factory>
</hibernate-configuration>

4- Hibernate & Java Persistence Annotation

Hibernate sử dụng các Annotation để mô tả các thông tin cho một Entity. Nó có thể sử dụng các annotation trong API của hibernate nằm trong package org.hibernate.annotations. Hoặc sử dụng các Annotation nằm trong package javax.persistence của Java Persistence API. Và thực tế là các Annotation của Java Persistence API được ưa chuộng hơn cả.

Trong phần này tôi liệt kê danh sách các Annotation thông dụng nhất của Java Persistence API tham gia vào chú thích cho Entity.
 

4.1- @Entity

@Entity sử dụng để chú thích một lớp là một Entity.
// Phần tử (element) name của @Entity là không bắt buộc.
// Việc chỉ định rõ name của @Entity cho phép viết ngắn câu HSQL

@Entity
@Table(name = "ACCOUNT")
public class Account implements Serializable {
    
}

// Phần tử (element) name của @Entity là không bắt buộc.
// Entity khớp với một bảng lấy theo tên theo thứ tự ưu tiên:
//  1 - name trong @Table
//  2 - name trong @Entity
//  3 - name của class.
// Việc chỉ định rõ name của @Entity cho phép viết ngắn câu HSQL

@Entity(name="AccTransaction")
@Table(name = "ACC_TRANSACTION")
public class AccTransaction implements Serializable {

}
 
Việc chỉ định rõ phần tử name của @Entity giúp bạn rút ngắn câu lệnh HSQL. Hãy xem ví dụ minh họa:
// @Entity chú thích trên class Account không chỉ định rõ phần tử name.
// Vì vậy câu HSQL bắt buộc phải viết:

String hsql1 = "Select o from "+ Account.class.getName() +" o ";


// @Entity chú thích trên class AccTransaction
// chỉ định rõ phần tử name = "AccTransaction"
// Vì vậy câu HSQL có thể viết ngắn gọn:

String hsql2 = "Select o from AccTransaction o";

4.2- @Table

Một Table trong DB có thể có nhiều giàng buộc duy nhất. @Table cũng cho phép bạn chú thích điều này.
// @Table cho phép chú thích tên bảng
// Các giàng buộc duy nhất trong bảng.
// Phần tử name không bắt buộc.
// Nếu bạn không chỉ rõ tên bảng trong phần tử name ...
// .. Hibernate sẽ dựa vào phần tử name của @Entity sau đó mới
// tới tên của class.

@Table( name = "invoice_header",
           uniqueConstraints = @UniqueConstraint(columnNames ={ "invoice_num" }   )
)
@Entity
public class InvoiceHeader  implements java.io.Serializable {

       private String invoiceNum;


      @Column(name = "invoice_num", nullable = false, length = 20)
      public String getInvoiceNum() {
          return this.invoiceNum;
      }

}

4.3- @Id

Ví dụ @Id tham gia vào chú thích ID (Identity) của Entity, nó tương đương với việc hiểu rằng cột đó là khóa chính của bảng ( Primary Key).
@Entity
@Table(name = "EMPLOYEE")
public class Employee implements Serializable {

    private Integer empId;

    // @Id chú thích đây là id của Entity.
    // Và EMP_ID chính là khóa chính (Primary Key) của bảng.

    @Id
    @GeneratedValue
    @Column(name = "EMP_ID")
    public Integer getEmpId() {
        return empId;
    }

     ......
}

4.4- @GeneratedValue

@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface GeneratedValue {

  // GenerationType: AUTO, TABLE, SEQUENCE, IDENTITY
  GenerationType strategy() default AUTO;

  String generator() default "";

}
@GeneratedValue được chú thích để Hibernate tự động tạo ra giá trị và gán vào cho một cột trong trường hợp insert mới một Entity vào database. Nó có thể gắn trên cột ID hoặc một cột nào đó.

Đôi khi nó cũng được chú thích cùng với @Generator

4.4.1- GenerationType.AUTO

// Chú thích @GeneratedValue tương đương với
// @GeneratedValue(strategy=GenerationType.AUTO)

@GeneratedValue
@Id
@Column(name = "EMP_ID")
public Integer getEmpId() {
    return empId;
}
Cột được chú thích bởi @GeneratedValue(strategy= AUTO) sẽ được gán giá trị tự động, giá trị đó có thể được sinh ra bởi SEQUENCE hoặc tự tăng (Nếu cột này có kiểu IDENTITY). Nó phụ thuộc vào loại DB.
Với Oracle, Postgres, hibernate nó sẽ gọi một Sequence có tên Hibernate_Sequence để tạo ra một giá trị tăng dần để gán giá trị cho cột này. Với các DB khác chẳng hạn như MySQL, DB2, SQL Server, Sysbase cột có thể là kiểu IDENTITY và giá trị của nó có thể tự tăng.

4.4.2- GenerationType.IDENTITY

Cột có kiểu IDENTITY chỉ được hỗ trợ bởi một vài loại cơ sở dữ liệu, không phải là tất cả, ví dụ MySQL, DB2, SQL Server, SybasePostgres. Oracle không hỗ trợ cột kiểu này.
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Id
@Column(name = "EMP_ID")
public Integer getEmpId() {
   return empId;
}

4.4.3- GenerationType.SEQUENCE

SEQUENCE là một đối tượng trong cơ sở dữ liệu, lưu trữ một giá trị tăng dần sau mỗi lần gọi nó để lấy giá trị tiếp theo, và được hỗ trợ bởi Oracle, DB2, và Postgres.
@GeneratedValue(strategy=GenerationType.SEQUENCE)
@Id
@Column(name = "EMP_ID")
public Integer getEmpId() {
   return empId;
}

4.4.4- GenerationType.TABLE

@Entity
public class Employee {
    ...
    @TableGenerator(
        name="empGen",
        table="ID_GEN_TABLE",
        pkColumnName="KEY_COLUMN",
        valueColumnName="VALUE_COLUMN",
        pkColumnValue="EMP_ID",
        allocationSize=1)
    @Id
    @GeneratedValue(strategy=TABLE, generator="empGen")
    public Long getEmpId()  {
       return empId;
    }
    ...
}


@Entity
public class Department {
    ...
    @TableGenerator(
        name="deptGen",
        table="ID_GEN_TABLE",
        pkColumnName="KEY_COLUMN",
        valueColumnName="VALUE_COLUMN",
        pkColumnValue="DEPT_ID",
        allocationSize=1)
    @Id
    @GeneratedValue(strategy=TABLE, generator="deptGen")
    public Long getDeptId()  {
       return deptId;
    }
    ...
}
 

Chú ý: Bạn có thể tùy biến tên bảng, tên cột (ID_GEN_TABLE, KEY_COLUMN, VALUE_COLUMN)

4.4.5- UUID

UUID là một lớp của Java cho phép bạn tạo ra một chuỗi 36 ký tự ngẫu nhiên. Và với 36 ký tự xác suất trùng nhau là vô cùng bé.

Bạn cũng có thể chú thích để Hibernate tạo ra một chuỗi ngẫu nhiên kiểu này gán cho giá trị của cột.
 // Sử dụng strategy = "uuid2".

@GenericGenerator(name="my-uuid", strategy = "uuid2")
@GeneratedValue(generator="my-uuid")
@Id
@Column(name = "EMP_ID", length = 36)
public String getEmpId()  {
   return empId;
}

4.5- @Column

@Column chú thích cho một cột, bao gồm các thông tin độ dài của cột, cho phép null hay không.
// Đây là một cột kiểu chuỗi, vì thế length luôn có ý nghĩa và cần thiết
// nullable mặc định là true
// length mặc định là 255

@Column(name = "FIRST_NAME", length = 20, nullable = false)
public String getFirstName() {
    return firstName;
}


// @Column không chỉ rõ phần tử length, mặc định nó là 255.

@Column(name = "DESCRIPTION", nullable = true )
public String getDescription() {
    return firstName;
}

// Với các cột kiểu số hoặc Date bạn có thể bỏ qua length
// (Nó không có ý nghĩa trong trường hợp này).

@Column(name = "PENDING_BALANCE")
public Float getPendingBalance() {
    return pendingBalance;
}




 

4.6- @Lob

@Lob thường chú thích cùng với @Column để nói rằng cột đó có kiểu BLOB hoặc CLOB.
// Chú ý rằng trong một số Database có phân biệt TINY, MEDIUM, LARGE BLOB/CLOB.
// Còn một số database thì không.
// Phần tử length trong @Column trong trường hợp này sẽ quyết định nó map
// vào BLOB/CLOB nào.
// Trong trường hợp cho phép BLOB/CLOB tối đa hãy để length = Integer.MAX_VALUE


// Method này trả về byte[]
// @Lob trong trường hợp này chú thích cho cột BLOB

@Lob
@Column(name = "IMAGE_VALUE", nullable = true, length = Integer.MAX_VALUE)
public byte[] getImageValue() {
    this.imageValue;
}

// Method này trả về String
// @Lob trong trường hợp này sẽ chú thích cho CLOB.

@Lob
@Column(name = "ARTICLE_CONTENT", nullable = true, length = Integer.MAX_VALUE)
public String getArticleContent() {
    this.articleContent;
}

4.7- @Temporal

@Temporal sử dụng để chú thích cho cột dữ liệu ngày tháng và thời gian (date time).
// @Temporal sử dụng chú thích cho cột có kiểu dữ liệu ngày tháng.
// Có 3 giá trị cho TemporalType:
// 1 - TemporalType.DATE
// 2 - TemporalType.TIME
// 3 - TemporalType.TIMESTAMP

@Temporal(TemporalType.DATE)
@Column(name = "START_DATE", nullable = false)
public java.util.Date getStartDate() {
    return startDate;
}

// TemporalType.DATE chú thích cột sẽ lưu trữ ngày tháng năm (bỏ đi thời gian)
// TemporalType.TIME chú thích cột sẽ lưu trữ thời gian (Giờ phút giây)
// TemporalType.TIMESTAMP chú thích cột sẽ lưu trữ ngày tháng và cả thời gian

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "FUNDS_AVAIL_DATE", nullable = false)
public java.util.Date getFundsAvailDate() {
      return fundsAvailDate;
}

4.8- @ManyToOne

@ManyToOne mô tả một quan hệ  N-1 (Nhiều - một), nó thường được sử dụng cùng với @JoinColumn.
@Entity
@Table(name = "ACCOUNT")
public class Account implements Serializable {

 
    private Branch openBranch;
 
   // Phần tử foreignKey giúp chỉ rõ tên Foreign Key trong DB.
   // Điều này sẽ giúp Hibernate tạo ra DB từ các Entity java một cách chính xác hơn.

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "OPEN_BRANCH_ID", nullable = false,
                 foreignKey = @ForeignKey(name = "ACCOUNT_BRANCH_FK"))
    public Branch getOpenBranch() {
        return openBranch;
    }

}
Hibernate có các công cụ cho phép bạn tạo ra các lớp Entity từ các bảng trong Database. Và Hibernate cũng cho phép bạn tạo ra bảng từ các lớp Entity, bao gồm cả giàng buộc giữa các bảng (Foreign Key). Chú thích @ForeignKey cho phép chỉ định rõ tên Foreign Key sẽ được tạo ra.
@ForeignKey được đưa vào JPA từ phiên bản 2.1
// Phần tử fetch có 2 giá trị
// 1 - FetchType.LAZY
// 2 - FetchType.EAGER

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "OPEN_BRANCH_ID", nullable = false,
             foreignKey = @ForeignKey(name = "ACCOUNT_BRANCH_FK"))
public Branch getOpenBranch() {
    return openBranch;
}

LAZY:

LAZY nói với Hibernate rằng, hãy tải dữ liệu một cách lười biếng.
Chẳng hạn bạn có một đối tượng Account, và gọi phương thức getOpenBranch() nó trả về một đối tượng Branch, đối tượng Branch chỉ trường (field) branchId được gán giá trị, các trường khác thì không.

Thực tế hibernate chưa tải dữ liệu từ bản ghi tương ứng của bảng BRANCH vào đối tượng này. Nó chỉ thực hiện truy vấn dữ liệu khi bạn làm gì đó với đối tượng Branch vừa có được, chẳng hạn gọi phương thức branch.getName().

EAGER:

EAGER nói với Hibernate hãy truy vấn toàn bộ các cột của bảng liên quan.
Chẳng hạn bạn có đối tượng Account, và gọi phương thức getOpenBranch() trả về đối tượng Branch đã có sẵn các giá trị cho các trường ( name, address, ...). Thực tế dữ liệu của nó có được cùng trong 1 lần truy vấn với bảng Account.
Bạn nên sử dụng LAZY thay vì EAGER vì lý do hiệu năng chương trình.

4.9- @OneToMany

@OneToMany là cách chú thích để lấy danh sách các bản ghi con của bản ghi hiện tại (Đây là quan hệ 1 nhiều). Nó là đảo ngược của chú thích @ManyToOne, và vì vậy nó dựa vào chú thích @ManyToOne để định nghĩa ra @OneToMany.
@Entity
@Table(name = "EMPLOYEE")
public class Employee implements Serializable {

    ....

    private Department department;
 
    // Quan hệ N-1 (Nhiều - Một) định nghĩa department.   

    @JoinColumn(name = "DEPT_ID", nullable = true,
              foreignKey = @ForeignKey(name = "EMPLOYEE_DEPARTMENT_FK"))    
    @ManyToOne(fetch = FetchType.LAZY)    
    public Department getDepartment() {
        return department;
    }
 

}



@Entity
@Table(name = "DEPARTMENT")
public class Department implements Serializable {

        .....

    private Set<Employee> employees = new HashSet<Employee>(0);

    // Quan hệ 1-N (Một - Nhiều) sử dụng mappedBy = "department"
    // đã định nghĩa ở quan hệ N-1 (phía trên).

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "department")
    public Set<Employee> getEmployees() {
        return employees;
    }
}

4.10- @OrderBy

@OrderBy Sử dụng để sắp xếp một tập hợp, vì vậy nó có thể được sử dụng cùng với @OneToMany:

@Entity
@Table(name = "DEPARTMENT")
public class Department implements Serializable {

        .....

    private Set<Employee> employees = new HashSet<Employee>(0);

    // Mặc định @OrderBy("empNo") tương đương với @OrderBy("empNo asc").
    // Nó tạo ra câu SQL: Select ... from Employee ... order by EMP_NO desc

    @OrderBy("empNo desc")
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "department")
    public Set<Employee> getEmployees() {
        return employees;
    }
}

4.11- @Transient

Hãy xem một tình huống:
@Entity
@Table(name="Employee")
public class Employee implements Serializable {
 
      .....

      @Column(name="FIRST_NAME", nullable =false , length = 20 )
      public String getFirstName()  {
           return this.firstName;
      }

      @Column(name="LAST_NAME", nullable =false , length = 20)
      public String getLastName()  {
           return this.lastName;
      }

     public String getFullName()  {
          return this.firstName+ " "+ this.lastName;
     }
}
Bạn muốn viết một phương thức getFullName(), phương thức này chỉ là một tính toán, không liên quan gì tới một cột nào dưới DB. Vì vậy bạn cần sử dụng @Transient để thông báo ý định của bạn với Hibernate.
@Transient
public String getFullName()  {
     return this.firstName+ " " + this.lastName;
}

@Transient
public boolean isManagerEmployee()  {
      return this.manager != null;
}

4.12- @Inheritance

Xem thêm các chuyên mục: