Tra cứu Java Hibernate
Công ty Vĩnh Cửu tuyển dụng lập trình viên Java

TÀI LIỆU ĐANG ĐƯỢC CẬP NHẬP (35%)

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:

Hibernate cho người mới bắt đầ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 Database nào đó, bạn cần có thư viện điều khiển loại database đó.
  • Oracle
  • MySQL
  • SQLServer
  • HSQL
  • ....
Tài liệu sau đây hướng dẫn bạn download các thư viện này.
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 trên database Oracle.

Trước hết bạn phải khai báo thư viện điều khiển database Oracle (Hướng dẫn ở trên).
Cấu hình Hibernate:
  • hibernate.cfg.xml
<?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 trên database MySQL

Trước hết bạn phải khai báo thư viện điều khiển database MySQL (Hướng dẫn ở trên).
Cấu hình Hibernate
  • hibernate.cfg.xml
<?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 trên database SQLServer

Trước hết bạn phải khai báo thư viện điều khiển database SQLServer (Hướng dẫn ở trên).
 
Cấu hình Hibernate (Sử dụng thư viện điều khiển JTDS)
  • hibernate.cfg.xml
<?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 cố gắng 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 chú thích một class 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 annotation @Entity giúp bạn rút ngắn câu 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 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 đi cùng với một Generator (bộ tạo giá tri)

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ể từ SEQUENCE hoặc tự tạo ra do cơ chế IDENTITY của cột. Nó phụ thuộc vào loại DB.

Với Oracle nó sẽ gọi từ Sequence Hibernate_Sequence để tạo ra một giá trị tăng dần gán vào cho cột. Với các DB khác chẳng hạn như MySQL, DB2, SQL Server, Sybase Postgres cột có thể là IDENTITY và giá trị của nó có thể tự tăng.

 

4.4.2- GenerationType.IDENTITY

Cột IDENTITY chỉ được support bởi một vài Database không phải là tất cả, ví dụ MySQL, DB2, SQL Server, Sybase và  Postgres. 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 database, lưu trữ một giá trị tăng dần sau mỗi lần gọi lấy giá trị tiếp theo, và được hỗ trợ bởi Oracle, DB2, and 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 class 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- @Blob @Clob

@Blob@Clob 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.
// @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 đi cặp 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 Tool cho phép bạn tạo ra các 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 Entity java, bao gồm cả giàng buộc giữa các bảng (Foreign Key). Annotation @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: Khái niệm load dữ liệu lười biếng. Nghĩa là bạn nhận được một đối tượng Account, và gọi getOpenBranch() nó trả về một đối tượng Branch, đối tượng này chỉ có cột branchId của nó là có giá trị, thực tế nó chưa load dữ liệu từ Record tương ứng của bảng BRANCH lên. Nó chỉ thực hiện điều đó khi bạn làm gì đó với đối tượng Branch vừa có được, chẳng hạn branch.getName().

EAGER: Khái niệm load dữ liệu lập tức. Nghĩa là bạn nhận được đối tượng Account, và gọi getOpenBranch() trả về đối tượng Branch đã có sẵn các giá trị thuộc tính (name, address, ...). Thực tế dữ liệu nó có được cùng trong 1 lần query với Account.

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 trong quan hệ 1 nhiều. Nó là đảo 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ể đi 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 method getFullName(), method này chỉ là một method tính toán, không liên quan gì tới một cột nào dưới DB. Tuy nhiên Hibernate lại không hiểu thế với tất cả các getter method, và mặc dù bạn cũng chẳng chú thích @Column trên method getFullName(), nhưng Hibenate vẫn hiểu là có với các thông số mặc định:
  • @Column(name="GETFULLNAME", nullable = true, length = 255)
Như vậy khi query dữ liệu nó sẽ query cả cột GETFULLNAME, vốn không tồn tại trong bảng Employee, và như vậy lỗi xẩy ra.

Hãy sử dụng @Transient để chú thích cho các getter method mà bạn muốn Hibernate bỏ qua method này.
@Transient
public String getFullName()  {
     return this.firstName+ " " + this.lastName;
}

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

4.12- @Inheritance