Trang chủHướng dẫnCác câu hỏi và trả lời phỏng vấn Hibernate thường gặp
Java

Các câu hỏi và trả lời phỏng vấn Hibernate thường gặp

CyStack blog 22 phút để đọc
CyStack blog02/06/2025
Reading Time: 22 minutes

Hibernate là một trong những công cụ ORM được sử dụng rộng rãi nhất cho các ứng dụng Java. Nó xuất hiện rất nhiều trong các giải pháp xử lý dữ liệu cho doanh nghiệp.

Bài viết này tổng hợp các câu hỏi và trả lời phỏng vấn Hibernate quan trọng để giúp bạn củng cố kiến thức trước buổi phỏng vấn sắp tới. Dù là người mới hay đã có kinh nghiệm, việc nắm vững kiến thức về Hibernate sẽ giúp bạn vượt qua buổi phỏng vấn dễ dàng hơn.

Các câu hỏi và trả lời phỏng vấn Hibernate

1. Hibernate Framework là gì?

Hibernate là một công cụ ORM viết bằng Java. Nó cung cấp một giải pháp để ánh xạ (map) các đối tượng miền ứng dụng sang các bảng cơ sở dữ liệu quan hệ và ngược lại.

Hibernate cung cấp một triển khai tham chiếu (reference implementation) của Java Persistence API (JPA). Lựa chọn này làm cho nó trở thành một lựa chọn tuyệt vời cho ORM với lợi ích là loose coupling (liên kết lỏng).

Ta có thể sử dụng Persistence API của Hibernate cho các thao tác CRUD. Hibernate cung cấp tùy chọn để ánh xạ các đối tượng Java thuần túy (Plain Old Java Objects – POJO) sang các bảng cơ sở dữ liệu truyền thống, bằng cách sử dụng JPA annotation cũng như cấu hình dựa trên XML. Việc cấu hình Hibernate rất linh hoạt và có thể được thực hiện từ file cấu hình XML cũng như bằng lệnh.

Hãy tham khảo bài hướng dẫn nhập môn Hibernate của chúng tôi để tìm hiểu thêm về công cụ ORM này.

2. Java Persistence API là gì?

Java Persistence API (JPA) cung cấp tiêu chuẩn đặc tả (specification) để quản lý dữ liệu quan hệ trong các ứng dụng. Phiên bản hiện tại của JPA là 2.1, được khởi xướng vào tháng 7 năm 2011 dưới tên JSR 338. JPA 2.1 được phê duyệt chính thức vào 22/5/2013.

Các đặc tả JPA được định nghĩa bằng các annotation trong package javax.persistence. Việc sử dụng JPA annotation giúp chúng ta viết code độc lập với triển khai cụ thể (implementation-independent code).

3. Những lợi ích quan trọng của việc sử dụng Hibernate là gì?

Một số lợi ích quan trọng của việc sử dụng Hibernate là:

  1. Hibernate loại bỏ tất cả mã dư thừa ( boilerplate code) đi kèm với JDBC và tự quản lý tài nguyên, nhờ đó chúng ta có thể tập trung vào viết logic nghiệp vụ.
  2. Hibernate hỗ trợ cả XML lẫn JPA annotation, giúp code của chúng ta độc lập với triển khai cụ thể.
  3. Hibernate cung cấp một ngôn ngữ truy vấn (HQL) mạnh mẽ tương tự như SQL. Tuy nhiên, HQL hoàn toàn mang tính hướng đối tượng và hiểu được các khái niệm như inheritance (kế thừa), polymorphism (đa hình) và association (liên kết).
  4. Hibernate là một dự án mã nguồn mở từ cộng đồng Red Hat và được sử dụng trên toàn thế giới. Điều này làm cho nó trở thành lựa chọn tốt hơn so với các giải pháp khác vì dễ tiếp cận, có nhiều tài liệu tham khảo, và dễ yêu cầu trợ giúp trên các diễn đàn mạng.
  5. Hibernate dễ dàng tích hợp với các framework Java EE khác. Nó phổ biến đến mức Spring Framework cung cấp hỗ trợ tích hợp sẵn Hibernate với các ứng dụng Spring.
  6. Hibernate hỗ trợ lazy initialization (khởi tạo lười) bằng cách sử dụng các đối tượng proxy và chỉ thực hiện các truy vấn cơ sở dữ liệu thực sự khi cần thiết.
  7. Cache của Hibernate giúp chúng ta đạt được hiệu suất tốt hơn.
  8. Hibernate rất phù hợp để dùng với các tính năng riêng của từng cơ sở dữ liệu, vì chúng ta cũng có thể thực thi các truy vấn SQL thuần.

Nhìn chung, Hibernate là lựa chọn ORM tốt nhất trên thị trường hiện tại vì nó chứa tất cả các tính năng mà bạn cần ở một công cụ ORM.

4. Ưu điểm của Hibernate so với JDBC là gì?

Một số ưu điểm nổi trội của Hibernate so với JDBC là:

  1. Hibernate loại bỏ rất nhiều boilerplate code đi kèm với JDBC API, giúp code gọn gàng và dễ đọc hơn.
  2. Hibernate hỗ trợ inheritance (kế thừa), association (liên kết) và collection (tập hợp). Các tính năng này không có trong JDBC API.
  3. Hibernate cung cấp quản lý transaction một cách ngầm định. Trên thực tế, hầu hết các truy vấn không thể thực thi bên ngoài một transaction. Trong JDBC API, chúng ta cần viết code để quản lý transaction bằng cách sử dụng commitrollback.
  4. JDBC API sử dụng SQLException. Đây là một checked exception (lỗi được kiểm tra bởi compiler) và ta phải sử dụng rất nhiều đoạn try-catch trong code cho nó. Điều này thường dẫn đến dư thừa về code khi sử dụng lệnh JDBC để quản lý transaction.Hibernate bao bọc (wrap) các ngoại lệ (exception) từ JDBC và ném ra JDBCException hoặc HibernateException. Đây là là các unchecked exception (lỗi không kiểm tra), do đó chúng ta không cần viết code để xử lý chúng. Hay nói cách khác, việc quản lý transaction tích hợp sẵn của Hibernate giúp ta loại bỏ việc sử dụng các khối try-catch.
  5. Hibernate Query Language (HQL) có tính hướng đối tượng và gần gũi với ngôn ngữ lập trình Java. Trong khi đối với JDBC, chúng ta sẽ cần viết các truy vấn SQL thuần.
  6. Hibernate hỗ trợ caching để giúp cải thiện hiệu suất. Các truy vấn JDBC không được cache nên hiệu suất thấp hơn.
  7. Hibernate cung cấp tùy chọn để tự động tạo bảng cơ sở dữ liệu, trong khi với JDBC, các bảng phải tồn tại sẵn trong cơ sở dữ liệu.
  8. Cấu hình Hibernate cho phép chúng ta sử dụng kết nối kiểu JDBC cũng như JNDI DataSource cho connection pool. Đây là một tính năng rất quan trọng trong ứng dụng cho doanh nghiệp và hoàn toàn không có trong JDBC API.
  9. Hibernate hỗ trợ JPA annotation, vì vậy code có tính độc lập với triển khai cụ thể và ta dễ dàng thay thế nó bằng các công cụ ORM khác. JDBC rất liên kết chặt chẽ với ứng dụng và khó thay thế hơn.

5. Kể tên một số interface quan trọng của Hibernate?

Một số interface quan trọng của Hibernate là:

  1. SessionFactory (org.hibernate.SessionFactory): Đây là một cache bất biến (immutable) và thread-safe của các ánh xạ đã được biên dịch cho một cơ sở dữ liệu. Ta chỉ cần khởi tạo SessionFactory một lần, sau đó có thể cache và tái sử dụng nó. Instance của SessionFactory được dùng để lấy các đối tượng Session cho các thao tác cơ sở dữ liệu.
  2. Session (org.hibernate.Session): Đây là một đối tượng đơn luồng (single-thread), tồn tại trong thời gian ngắn, đại diện cho một phiên làm việc giữa ứng dụng và persistent store (kho lưu trữ bền vững). Nó wrap JDBC java.sql.Connection và hoạt động như một factory cho org.hibernate.Transaction. Ta chỉ nên mở session khi cần thiết và đóng nó ngay sau khi sử dụng xong. Đối tượng Session là interface giữa mã ứng dụng Java và Hibernate, cung cấp các phương thức cho hoạt động CRUD.
  3. Transaction (org.hibernate.Transaction): Đây là một đối tượng đơn luồng, tồn tại trong thời gian ngắn, được ứng dụng sử dụng để xác định các đơn vị công việc nhỏ nhất. Nó giúp trừu tượng hóa ứng dụng khỏi các transaction của JDBC hoặc JTA. Một org.hibernate.Session có thể bao gồm nhiều org.hibernate.Transaction trong một số trường hợp.

6. File cấu hình Hibernate là gì?

File cấu hình Hibernate chứa các cấu hình đặc thù cho cơ sở dữ liệu và được sử dụng để khởi tạo SessionFactory. Ở đó ta cung cấp thông tin đăng nhập cơ sở dữ liệu hoặc thông tin tài nguyên JNDI. Một số phần quan trọng khác của file cấu hình Hibernate là thông tin Dialect để Hibernate biết loại cơ sở dữ liệu và chi tiết về file hoặc class ánh xạ.

7. File mapping của Hibernate là gì?

File mapping của Hibernate được dùng để định nghĩa các ánh xạ (mapping) giữa trường của entity bean và cột trong bảng cơ sở dữ liệu. Tuy ta có thể dùng các annotation JPA cho việc mapping, đôi khi sử dụng file mapping XML lại tốt hơn hơn khi ta làm việc với các class từ thư viện bên thứ ba và không thể sử dụng annotation.

8. Kể tên một số annotation quan trọng dùng cho việc mapping của Hibernate?

Hibernate hỗ trợ các annotation JPA và cũng có một số annotation riêng trong package org.hibernate.annotations. Một số annotation quan trọng của JPA và Hibernate thường dùng là:

  1. javax.persistence.Entity: Dùng với các lớp model để chỉ định rằng chúng là các entity bean.
  2. javax.persistence.Table: Dùng với entity bean để định nghĩa tên bảng tương ứng trong cơ sở dữ liệu.
  3. javax.persistence.Access: Dùng để định nghĩa kiểu truy cập: field (trường, giá trị mặc định) hoặc property (thuộc tính). Nếu muốn Hibernate sử dụng các phương thức getter/setter, ta cần đặt giá trị là property.
  4. javax.persistence.Id: Dùng để định nghĩa khóa chính trong entity bean.
  5. javax.persistence.EmbeddedId: Dùng để định nghĩa khóa chính phức (composite) hợp trong entity bean.
  6. javax.persistence.Column: Dùng để định nghĩa tên cột trong bảng cơ sở dữ liệu.
  7. javax.persistence.GeneratedValue: Dùng để định nghĩa cách tạo khóa chính. Sử dụng cùng với enum javax.persistence.GenerationType.
  8. javax.persistence.OneToOne: Dùng để định nghĩa ánh xạ một-một giữa hai entity bean. Chúng ta cũng có các annotation tương tự như OneToMany, ManyToOneManyToMany.
  9. org.hibernate.annotations.Cascade: Dùng để định nghĩa hành vi cascading giữa hai entity bean. Sử dụng cho các ánh xạ cùng với org.hibernate.annotations.CascadeType.
  10. javax.persistence.PrimaryKeyJoinColumn: Dùng để định nghĩa thuộc tính cho khóa ngoại. Sử dụng cùng với org.hibernate.annotations.GenericGeneratororg.hibernate.annotations.Parameter.

Dưới đây là hai class minh họa cách sử dụng các annotation này.

package com.journaldev.hibernate.model;

import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cascade;

@Entity
@Table(name = "EMPLOYEE")
@Access(value=AccessType.FIELD)
public class Employee {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "emp_id")
	private long id;

	@Column(name = "emp_name")
	private String name;

	@OneToOne(mappedBy = "employee")
	@Cascade(value = org.hibernate.annotations.CascadeType.ALL)
	private Address address;

	//getter setter methods
}

package com.journaldev.hibernate.model;

import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

@Entity
@Table(name = "ADDRESS")
@Access(value=AccessType.FIELD)
public class Address {

	@Id
	@Column(name = "emp_id", unique = true, nullable = false)
	@GeneratedValue(generator = "gen")
	@GenericGenerator(name = "gen", strategy = "foreign", parameters = { @Parameter(name = "property", value = "employee") })
	private long id;

	@Column(name = "address_line1")
	private String addressLine1;

	@OneToOne
	@PrimaryKeyJoinColumn
	private Employee employee;

	//getter setter methods
}

9. Hibernate SessionFactory là gì và làm sao để cấu hình nó?

SessionFactory là một class factory được dùng để lấy các đối tượng Session. Nó chịu trách nhiệm đọc các tham số cấu hình Hibernate, kết nối đến cơ sở dữ liệu và cung cấp các đối tượng Session. Thông thường, một ứng dụng chỉ có một thực thể của SessionFactory duy nhất, và các thread xử lý request từ client sẽ lấy các thực thể Session từ factory này.

Trạng thái nội tại của một SessionFactory sẽ được thiết lập và bất biến một khi đã được tạo. Nó bao gồm tất cả metadata về ánh xạ Object/Relational. SessionFactory cũng cung cấp các phương thức để lấy metadata của Class và instance Statistics nhằm thu thập thống kê về việc thực thi query, chi tiết về cache cấp hai, v.v.

10. Hibernate SessionFactory có thread-safe không?

Trạng thái nội tại của SessionFactory là bất biến, do đó nó mang tính an toàn luồng (thread-safe). Nhiều thread có thể truy cập đồng thời vào SessionFactory để lấy các instance Session.

11. Hibernate Session là gì và làm thế nào để có được nó?

Hibernate Session là giao diện giữa tầng ứng dụng Java và Hibernate. Đây là interface cốt lõi được sử dụng để thực hiện các thao tác cơ sở dữ liệu. Vòng đời của một session gắn liền với điểm bắt đầu và kết thúc của một transaction.

Session cung cấp các phương thức để thực hiện các thao tác tạo, đọc, cập nhật và xóa (CRUD) đối với một đối tượng persistent. Chúng ta có thể thực thi các query HQL, query SQL thuần và tạo tiêu chí bằng cách sử dụng đối tượng Session.

12. Hibernate Session có thread-safe không?

Đối tượng Hibernate Session không có tính thread-safe. Mỗi thread nên có instance session của riêng mình và đóng nó lại sau khi công việc hoàn tất.

13. Sự khác biệt giữa openSession và getCurrentSession là gì?

Phương thức getCurrentSession() của Hibernate SessionFactory trả về session được gắn với context. Tuy nhiên, để hoạt động, ta cần cấu hình điều này trong file cấu hình Hibernate. Vì đối tượng session này thuộc về context của Hibernate, ta không cần phải đóng nó. Khi SessionFactory được đóng, đối tượng session này cũng sẽ tự động được đóng.

<property name="hibernate.current_session_context_class">thread</property>

Phương thức openSession() của SessionFactory luôn tạo một session mới. Ta nên đóng đối tượng session này sau khi hoàn tất mọi thao tác cơ sở dữ liệu. Trong môi trường đa luồng, ta nên mở một session mới cho mỗi request. Có một phương thức khác là openStatelessSession() trả về một session phi trạng thái (stateless).

14. Sự khác biệt giữa phương thức get() và load() của Session của Hibernate là gì?

Hibernate session cung cấp các phương thức khác nhau để tải dữ liệu từ cơ sở dữ liệu, trong đó get()load() là hai phương thức được sử dụng nhiều nhất. Thoạt nhìn, chúng có vẻ tương tự nhau. Tuy nhiên, có một số khác biệt quan trọng cần lưu ý:

  1. get() load dữ liệu ngay khi được gọi, trong khi load() trả về một đối tượng proxy và chỉ load dữ liệu khi thực sự cần thiết. Do đó, load() hỗ trợ tốt hơn kỹ thuật lazy loading (tải lười, tải chậm).
  2. load() sẽ cho ra lỗi (exception) khi không tìm thấy dữ liệu, ta chỉ nên sử dụng nó khi biết chắc dữ liệu tồn tại.
  3. Ta nên sử dụng get() khi muốn đảm bảo dữ liệu chắc chắn tồn tại trong database (nó sẽ trả về null nếu không tìm thấy).

15. Caching trong Hibernate là gì? Giải thích về first-level cache?

Như tên gọi của nó, Hibernate cache dữ liệu của query để giúp ứng dụng của chúng ta hoạt động nhanh hơn. Cache này rất có ích trong việc tăng hiệu suất nếu được sử dụng đúng cách.

Mục đich chính của việc sử dụng cache là giảm số lượng query đến cơ sở dữ liệu, từ đó giảm thời gian xử lý của ứng dụng. Cache lớp thứ nhất (first-level) gắn liền với đối tượng Session. Nó được bật mặc định và không có cách nào để tắt nó đi.

Tuy nhiên, Hibernate cung cấp các phương thức cho phép ta xóa các đối tượng cụ thể khỏi cache hoặc thậm chí xóa hoàn toàn cache. Các đối tượng được cache trong một session sẽ không dùng được với các session khác. Khi session đóng lại, tất cả các đối tượng đã được cache cũng sẽ mất đi.

16. Làm thế nào để cấu hình cache cấp hai sử dụng EHCache?

EHCache là một lựa chọn hàng đầu để sử dụng cache cấp hai của Hibernate. Ta sẽ cần thực hiện các bước sau để kích hoạt EHCache:

  • Thêm dependency hibernate-ehcache vào project Maven. Nếu không dùng Maven, hãy thêm các file JAR tương ứng.
  • Thêm các thuộc tính dưới đây vào file cấu hình Hibernate.
<dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-ehcache</artifactId>
        <version>4.3.5.Final</version>
</dependency>
  • Tạo file cấu hình EHCache. Một file myehcache.xml mẫu sẽ có dạng như sau:
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
         
<!-- For singleton factory -->
<!-- <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>
-->
          
<!-- enable second level cache and query cache -->
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="hibernate.cache.use_query_cache">true</property>
<property name="net.sf.ehcache.configurationResourceName">/myehcache.xml</property>
  • Thêm annotation @Cache vào các entity bean cùng với chiến lược cache sẽ sử dụng. Ví dụ:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="<https://www.w3.org/2001/XMLSchema-instance>"
    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
    monitoring="autodetect" dynamicConfig="true">
 
    <diskStore path="java.io.tmpdir/ehcache" />
 
    <defaultCache maxEntriesLocalHeap="10000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
        maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU" statistics="true">
        <persistence strategy="localTempSwap" />
    </defaultCache>
 
    <cache name="employee" maxEntriesLocalHeap="10000" eternal="false"
        timeToIdleSeconds="5" timeToLiveSeconds="10">
        <persistence strategy="localTempSwap" />
    </cache>
 
    <cache name="org.hibernate.cache.internal.StandardQueryCache"
        maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
        <persistence strategy="localTempSwap" />
    </cache>
 
    <cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
        maxEntriesLocalHeap="5000" eternal="true">
        <persistence strategy="localTempSwap" />
    </cache>
</ehcache>

17. Các trạng thái khác nhau của một entity bean là gì?

Một instance của entity bean có thể tồn tại ở một trong ba trạng thái sau:

  1. Transient: Khi một đối tượng chưa bao giờ được persist (lưu trữ lâu dài) hoặc chưa được gắn với bất kỳ session nào, nó ở trạng thái transient (tạm thời). Các instance transient có thể được chuyển sang trạng thái persistent bằng cách gọi save(), persist() hoặc saveOrUpdate(). Ngược lại, các instance persistent có thể chuyển thành transient bằng cách gọi delete().
  2. Persistent: Khi một đối tượng được gắn với một session cụ thể, nó ở trạng thái persistent. Mọi instance được trả về bởi phương thức get() hoặc load() đều ở trạng thái persistent.
  3. Detached: Khi một đối tượng từng ở trạng thái persistent nhưng hiện không còn được gắn với session nào, nó ở trạng thái detached. Các instance detached có thể được chuyển lại trạng thái persistent bằng cách gọi update(), saveOrUpdate(), lock() hoặc replicate(). Trạng thái của một instance transient hoặc detached cũng có thể được chuyển thành một instance persistent mới bằng cách gọi merge().

18. Tác dụng của phương thức merge() trong Hibernate Session là gì?

Phương thức merge() của Hibernate có thể được dùng để cập nhật các giá trị hiện có. Tuy nhiên, phương thức này tạo ra một bản sao từ đối tượng entity được truyền vào và trả về bản sao đó. Đối tượng được trả về là một phần của persistent context và được theo dõi mọi thay đổi, trong khi đối tượng gốc được truyền vào thì không.

19. Sự khác biệt giữa các phương thức Hibernate save(), saveOrUpdate() và persist() là gì?

Phương thức save() của Hibernate có thể được dùng để lưu entity vào cơ sở dữ liệu. Vấn đề với save() là nó có thể được gọi mà không cần transaction. Nếu có các entity được ánh xạ (mapping), thì chỉ đối tượng chính được lưu và có thể gây ra tình trạng không nhất quán dữ liệu. Ngoài ra, save() trả về ID được sinh ra ngay lập tức.

Phương thức persist() tương tự như save() nhưng bắt buộc phải có transaction. Nó tốt hơn save() vì ta không thể sử dụng nó bên ngoài phạm vi của transaction, do đó tất cả các ánh xạ đối tượng đều được bảo toàn. persist() cũng không trả về ID được sinh ra ngay lập tức. Việc lưu trữ dữ liệu persistence chỉ xảy ra khi cần thiết.

Phương thức saveOrUpdate() tạo ra query INSERT hoặc UPDATE tùy thuộc vào dữ liệu được cung cấp. Nếu dữ liệu đã tồn tại trong cơ sở dữ liệu, query UPDATE sẽ được thực thi.

Chúng ta cũng có thể sử dụng saveOrUpdate() mà không cần transaction. Nhưng phải nhắc lại một lần nữa, bạn có thể gặp phải vấn đề với việc các đối tượng được ánh xạ không được lưu nếu session không được flush.

20. Điều gì sẽ xảy ra nếu chúng ta không có constructor không tham số (no-args constructor) trong entity bean?

Hibernate sử dụng Reflection API để tạo instance của entity bean, thường khi phương thức get() hoặc load() được gọi. Phương thức Class.newInstance() được dùng cho việc này và nó yêu cầu một constructor không tham số (no-args constructor).

Do đó, nếu entity bean không có constructor này, Hibernate sẽ không thể khởi tạo một instance cho nó và sẽ cho ra lỗi HibernateException.

21. Đâu là sự khác biệt giữa collection được sort và collection được order? Cái nào tốt hơn?

Khi ta sử dụng các thuật toán sắp xếp của Collection API để sắp xếp một collection, nó được gọi là sorted list. Đối với các collection nhỏ, điều này không gây ra nhiều gánh nặng xử lý. Nhưng với các collection lớn, nó có thể dẫn đến hiệu suất chậm và lỗi OutOfMemoryError.

Ngoài ra, các entity bean cần triển khai interface Comparable hoặc Comparator để việc sắp xếp này hoạt động được. Nếu chúng ta sử dụng Hibernate để load dữ liệu collection từ database, ta có thể dùng Criteria API của nó để sử dụng mệnh đề “order by” nhằm có được ordered list.

Ordered list tốt hơn sorted list vì việc sắp xếp thực tế được thực hiện ở tầng database, vốn nhanh hơn và không gây ra các vấn đề về bộ nhớ.

22. Các loại collection trong Hibernate là gì?

Có năm loại collection trong Hibernate được sử dụng cho các ánh xạ quan hệ một-nhiều (one-to-many):

  • Bag
  • Set
  • List
  • Array
  • Map

23. Làm thế nào để triển khai join trong Hibernate?

Có nhiều cách để triển khai join trong Hibernate:

  • Sử dụng các association như one-to-one (một-một), one-to-many (một-nhiều), v.v.
  • Sử dụng JOIN trong truy vấn HQL. Có một dạng khác là “join fetch” để load đồng thời dữ liệu liên quan, không có lazy loading.
  • Thực thi query SQL thuần túy và sử dụng từ khóa join.

24. Tại sao chúng ta không nên đặt Entity Class là final?

Hibernate sử dụng các proxy class để lazy loading dữ liệu, tức là chỉ load dữ liệu khi cần thiết. Điều này được thực hiện bằng cách kế thừa (extend) entity bean. Nếu entity bean là final, thì lazy loading sẽ không thể thực hiện được, dẫn đến hiệu suất thấp.

25. HQL là gì và lợi ích của nó là gì?

Hibernate đi kèm với một ngôn ngữ truy vấn hướng đối tượng mạnh mẽ có tên là Hibernate Query Language (HQL). Nó rất giống với SQL ngoại trừ việc chúng ta sử dụng Object thay vì tên bảng. Điều này giúp làm cho nó gần gũi hơn với phương thức lập trình hướng đối tượng.

Ngôn ngữ truy vấn Hibernate không phân biệt chữ hoa chữ thường, ngoại trừ tên class và biến Java. Vì vậy, SeLeCT cũng có tác dụng giống như sELEctSELECT, nhưng com.journaldev.model.Employee lại không trùng với com.journaldev.model.EMPLOYEE.

Tuy các truy vấn HQL được cache, chúng ta nên tránh lạm dụng việc này. Nếu không ta sẽ phải quan tâm đến việc quản lý các association. Dù sao đó là một lựa chọn tốt hơn so với truy vấn SQL native nhờ vào phương pháp tiếp cận hướng đối tượng.

26. Query cache trong Hibernate là gì?

Hibernate tạo một vùng cache cho các bộ kết quả (resultset) của query, và nó tích hợp chặt chẽ với second-level cache của Hibernate. Đây là một tính năng không bắt buộc và đòi hỏi ta phải thêm các bước khác trong code. Nó chỉ hữu ích cho các query được chạy thường xuyên với cùng một bộ tham số.

Trước hết, chúng ta cần cấu hình thuộc tính dưới đây trong file cấu hình Hibernate:

<property name="hibernate.cache.use_query_cache">true</property>

Trong code, ta cần sử dụng phương thức setCacheable(true) của Query:

Query query = session.createQuery("from Employee");
query.setCacheable(true);
query.setCacheRegion("ALL_EMP");

27. Chúng ta có thể thực thi truy vấn SQL thuần túy trong Hibernate không?

Hibernate cung cấp tùy chọn để chạy các truy vấn SQL thuần túy thông qua việc sử dụng đối tượng SQLQuery. Tuy nhiên, bình thường ta không nên dùng cách này bởi nó làm mất đi các lợi ích liên quan đến association và first-level caching trong Hibernate.

28. Lợi ích của việc hỗ trợ truy vấn SQL thuần túy trong Hibernate là gì?

Truy vấn SQL thuần túy có ích khi chúng ta muốn thực thi các truy vấn đặc thù của database mà Hibernate API không hỗ trợ, chẳng hạn như query hint (gợi ý truy vấn) hoặc từ khóa CONNECT trong Oracle Database.

29. Giải thích truy vấn SQL có tên (Named SQL Query) ?

Hibernate cung cấp named query mà chúng ta có thể định nghĩa ở một nơi và sử dụng chúng ở bất kỳ đâu trong code. Chúng ta có thể tạo các named query cho cả HQL và SQL thuần. Named query có thể được định nghĩa trong các file ánh xạ Hibernate hoặc thông qua việc sử dụng các annotation JPA @NamedQuery@NamedNativeQuery.

30. Lợi ích của truy vấn SQL có tên là gì?

Named query query giúp chúng ta nhóm các truy vấn tại một nơi trung tâm thay vì để chúng rải rác khắp nơi trong code. Cú pháp của nó được kiểm tra khi Hibernate SessionFactory được tạo, giúp ứng dụng phát hiện lỗi sớm (fail fast) trong trường hợp có bất kỳ lỗi nào trong các named query.

Named query có phạm vi global, nghĩa là một khi đã được định nghĩa, nó có thể được sử dụng trong toàn bộ ứng dụng. Tuy nhiên, một trong những nhược điểm chính của nó là khó debug vì chúng ta cần phải tìm ra nơi mà nó được định nghĩa.

31. Lợi ích của Criteria API là gì?

Criteria API trong Hibernate là một API có tính hướng đối tượng hơn để truy vấn database và nhận kết quả. Chúng ta không thể sử dụng Criteria để chạy các truy vấn update, delete hoặc bất kỳ câu lệnh DDL nào. Nó chỉ được sử dụng để lấy kết quả từ database với cách tiếp cận mang tính hướng đối tượng hơn.

Một số ứng dụng phổ biến của Criteria API là:

  • Criteria API cung cấp Projection mà chúng ta có thể sử dụng cho các hàm tổng hợp như sum(), min(), max(), v.v.
  • Criteria API có thể được sử dụng với ProjectionList để chỉ lấy các cột được chọn.
  • Criteria API có thể được sử dụng cho các truy vấn join bằng cách join nhiều bảng. Các phương thức hữu ích là createAlias(), setFetchMode()setProjection().
  • Criteria API có thể được sử dụng để lấy kết quả với các điều kiện, phương thức hữu ích là add() nơi chúng ta có thể thêm các Restrictions.
  • Criteria API cung cấp phương thức addOrder() mà chúng ta có thể sử dụng để sắp xếp kết quả.

32. Làm thế nào để log các truy vấn SQL do Hibernate tạo ra vào file log?

Ta có thể thiết lập thuộc tính sau cho cấu hình Hibernate để log các truy vấn SQL:

<property name="hibernate.show_sql">true</property>

Tuy nhiên, chúng ta chỉ nên sử dụng nó trong môi trường dev hoặc testing và tắt nó trong production.

33. Hibernate Proxy là gì và nó giúp lazy loading như thế nào?

Hibernate sử dụng đối tượng proxy để hỗ trợ lazy loading. Về cơ bản, khi bạn load dữ liệu từ các bảng, Hibernate không tải tất cả các đối tượng được ánh xạ. Ngay khi bạn tham chiếu đến một đối tượng con hoặc đối tượng tra cứu thông qua các phương thức getter, nếu entity được liên kết không có trong session cache, thì code proxy sẽ đi đến database và load đối tượng được liên kết. Nó sử dụng Javassist để triển khai đối tượng entity của bạn dưới dạng subclass một cách hiệu quả và linh hoạt.

34. Làm thế nào để triển khai các mối quan hệ dữ liệu trong Hibernate?

Chúng ta có thể dễ dàng triển khai các mối quan hệ one-to-one, one-to-many và many-to-many trong Hibernate. Điều này có thể được thực hiện bằng cách sử dụng các annotation JPA cũng như cấu hình dựa trên XML.

35. Quản lý transaction trong Hibernate như thế nào?

Ta có thể quản lý transaction trong Hibernate rất dễ dàng vì hầu hết các thao tác không được phép thực hiện bên ngoài một transaction. Vì vậy, sau khi lấy session từ SessionFactory, chúng ta có thể gọi session.beginTransaction() để bắt đầu transaction. Phương thức này trả về một tham chiếu Transaction mà chúng ta có thể sử dụng sau này để commit (xác nhận) hoặc rollback (hủy bỏ) transaction.

Nhìn chung, quản lý transaction trong Hibernate tốt hơn trong JDBC vì chúng ta không cần phải dựa vào exception để rollback. Bất kỳ exception nào được ném ra bởi các phương thức của session sẽ tự động rollback transaction.

36. Cascading (lan truyền) là gì và có những loại cascading nào?

Khi chúng ta có mối quan hệ giữa các entity, chúng ta cần định nghĩa cách các thao tác sẽ ảnh hưởng đến các entity khác như thế nào. Ta thực hiện điều này bằng cơ chế cascading.

Có nhiều loại cascading khác nhau. Dưới đây là một ví dụ đơn giản về việc áp dụng cascading giữa các entity chính và phụ.

import org.hibernate.annotations.Cascade;

@Entity
@Table(name = "EMPLOYEE")
public class Employee {

@OneToOne(mappedBy = "employee")
@Cascade(value = org.hibernate.annotations.CascadeType.ALL)
private Address address;

}

Lưu ý rằng các hằng số enum CascadeType hơi khác so với javax.persistence.CascadeType của JPA. Vì vậy chúng ta cần sử dụng CascadeType và các annotation Cascade cho các ánh xạ như trong ví dụ trên.

Các loại cascading thường được sử dụng như được định nghĩa trong enum CascadeType là:

  1. None: Không Cascading, đây không phải là một loại nhưng khi chúng ta không định nghĩa bất kỳ cascading nào thì không có thao tác nào ở entity cha ảnh hưởng đến entity con.
  2. ALL: Kế thừa các thao tác save, delete, update, evict, lock, replicate, merge, persist. Về cơ bản là tất cả mọi thứ.
  3. SAVE_UPDATE: Kế thừa save và update, chỉ có trong Hibernate.
  4. DELETE: Tương ứng với action DELETE thuần của Hibernate, chỉ có trong Hibernate.
  5. DETATCH, MERGE, PERSIST, REFRESH và REMOVE: Cho các thao tác tương tự.
  6. LOCK: Tương ứng với action LOCK của Hibernate.
  7. REPLICATE: Tương ứng với action REPLICATE của Hibernate.

37. Làm thế nào để tích hợp logging của log4j vào ứng dụng Hibernate?

Hibernate 4 sử dụng JBoss cho việc logging thay vì slf4j như các phiên bản trước đó. Để dùng log4j, ta cần thực hiện các bước sau:

  • Thêm các dependency của log4j cho dự án Maven. Nếu không dùng Maven thì thêm các file jar tương ứng.
  • Tạo file cấu hình log4j.xml hoặc log4j.properties và đặt nó trong classpath. Bạn có thể đặt tên file tùy ý vì chúng ta sẽ load nó ở bước tiếp theo.
  • Đối với các dự án riêng lẻ, sử dụng static block để cấu hình log4j bằng DOMConfigurator hoặc PropertyConfigurator. Đối với các ứng dụng web, có thể sử dụng ServletContextListener để cấu hình nó.

Đến đây thiết lập của chúng ta đã sẵn sàng. Tiếp theo, tạo instance của org.apache.log4j.Logger trong các class Java và bắt đầu logging bằng log4j.

38. Làm thế nào để sử dụng JNDI DataSource của máy chủ ứng dụng với Hibernate?

Đối với các ứng dụng web, luôn tốt nhất là để servlet container quản lý connection pool. Đó là lý do tại sao chúng ta định nghĩa JNDI resource cho DataSource và có thể sử dụng nó trong ứng dụng web.

Việc sử dụng trong Hibernate rất dễ. Tất cả những gì chúng ta cần là loại bỏ tất cả các thuộc tính cụ thể của database và sử dụng thuộc tính dưới đây để cung cấp tên JNDI DataSource.

<property name="hibernate.connection.datasource">java:comp/env/jdbc/MyLocalDB</property>

39. Làm thế nào để tích hợp Hibernate và Spring?

Spring là một trong những framework của Java EE được sử dụng nhiều nhất, trong khi Hibernate là ORM phổ biến nhất. Đó là lý do tại sao sự kết hợp Spring với Hibernate được sử dụng rất nhiều trong các ứng dụng doanh nghiệp.

Điều hay nhất khi sử dụng Spring là nó cung cấp hỗ trợ tích hợp sẵn cho Hibernate với module Spring ORM. Làm theo các bước sau đây để tích hợp Spring và Hibernate với nhau:

  1. Thêm các dependency hibernate-entitymanager, hibernate-corespring-orm.
  2. Tạo các Model class và các triển khai DAO tương ứng cho các thao tác database. Lưu ý rằng các DAO class sẽ sử dụng SessionFactory được inject bởi cấu hình Spring Bean.
  3. Nếu bạn đang sử dụng Hibernate 3, bạn cần cấu hình org.springframework.orm.hibernate3.LocalSessionFactoryBean hoặc org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean trong file cấu hình Spring Bean. Đối với Hibernate 4, có một class duy nhất là org.springframework.orm.hibernate4.LocalSessionFactoryBean cần được cấu hình.
  4. Lưu ý rằng chúng ta không cần sử dụng Transaction Management của Hibernate. Ta có thể để trình quản lý transaction kiểu miêu tả (declarative) của Spring để xử lý việc này bằng cách sử dụng annotation @Transactional.

40. Lớp HibernateTemplate là gì?

Đây là dạng câu hỏi thường dùng để đánh giá bạn có cập nhật kiến thức mới gần đây hay không. Khi tích hợp Spring và Hibernate ở các phiên bản cũ, Spring ORM cung cấp hai helper class: HibernateDaoSupportHibernateTemplate. Ta sử dụng chúng để lấy session từ Hibernate và hưởng lợi từ cơ chế quản lý transaction của Spring.

Tuy nhiên, từ Hibernate 3.0.1, chúng ta có thể sử dụng phương thức SessionFactory.getCurrentSession() để lấy session hiện tại và sử dụng nó để hưởng lợi từ cơ chế quản lý transaction của Spring. Qua các ví dụ trên, bạn đã thấy việc dùng phương thức này dễ dàng như thế nào và đó là lý do tại sao chúng ta không nên sử dụng các class trên nữa.

Một lợi ích khác của HibernateTemplate là dịch exception. Tuy nhiên, ta cũng có thể làm được điều đó bằng annotation @Repository với các service class như đã được trình bày trong ví dụ Spring MVC ở trên.

41. Làm thế nào để tích hợp Hibernate với ứng dụng web Servlet hoặc Struts2?

Việc tích hợp Hibernate với Servlet hoặc Struts2 cần được thực hiện bằng cách sử dụng ServletContextListener.

42. Những design pattern nào được sử dụng trong Hibernate?

Một số design pattern được sử dụng trong Hibernate là:

  • Mô hình miền (Domain model): Một mô hình đối tượng (object model) của domain kết hợp cả hành vi và dữ liệu.
  • Ánh xạ dữ liệu (Data mapper): Một layer gồm các mapper (bộ ánh xạ) di chuyển dữ liệu giữa các object và database trong khi giữ chúng độc lập với nhau và với chính mapper.
  • Proxy cho lazy loading.
  • Factory trong SessionFactory.

43. Các thực hành tốt nhất (best practice) nào nên tuân theo trong Hibernate?

Một số best practice trong Hibernate bạn nên biết là:

  • Luôn kiểm tra quyền truy cập trường key chính. Nếu nó được tạo ở tầng database thì bạn không nên có setter cho trường này.
  • Theo mặc định, Hibernate đặt giá trị các trường trực tiếp mà không sử dụng setter. Vì vậy, nếu bạn muốn Hibernate sử dụng setter, hãy đảm bảo quyền truy cập phù hợp được định nghĩa là @Access(value=AccessType.PROPERTY).
  • Nếu loại truy cập (access type) là property, hãy đảm bảo các annotation được sử dụng với các phương thức getter chứ không phải phương thức setter. Tránh trộn lẫn việc sử dụng annotation trên cả trường và phương thức getter.
  • Chỉ sử dụng truy vấn SQL thuần khi không thể thực hiện bằng HQL, chẳng hạn như sử dụng tính năng đặc thù của database.
  • Nếu bạn phải sắp xếp collection, hãy sử dụng ordered list thay vì sắp xếp nó bằng Collection API.
  • Sử dụng named query một cách khôn ngoan và giữ chúng ở một nơi duy nhất để dễ debug. Chỉ sử dụng chúng cho các truy vấn được dùng nhiều. Đối với truy vấn cụ thể của entity, bạn có thể giữ chúng trong chính entity bean đó.
  • Đối với các ứng dụng web, luôn cố gắng sử dụng JNDI DataSource thay vì cấu hình để tạo connection trong Hibernate.
  • Tránh các mối quan hệ nhiều-nhiều (many-to-many). Có thể thay thế bằng cách sử dụng các mối quan hệ một-nhiều (one-to-many) và nhiều-một (many-to-one) hai chiều.
  • Đối với các collection, hãy cố gắng sử dụng list, map và set. Tránh dùng array vì bạn không nhận được lợi ích từ việc lazy loading.
  • Không coi exception có thể phục hồi được. Hãy rollback transaction và đóng session. Nếu bạn không làm điều này, Hibernate không thể đảm bảo rằng trạng thái trong bộ nhớ phản ánh chính xác trạng thái bền vững (persistent state).
  • Ưu tiên mẫu DAO để công khai (expose) các phương thức khác nhau có thể được sử dụng với entity bean.
  • Ưu tiên lazy fetching cho các association.

44. Hibernate Validator là gì?

Kiểm định dữ liệu (data validation) là một phần không thể thiếu của bất kỳ ứng dụng nào. Bước này xuất hiện ở tầng presentation (sử dụng Javascript) và sau đó ở phía máy chủ trước khi nó xử lý dữ liệu.

Data validation cũng xảy ra trước khi lưu dữ liệu lâu dài để đảm bảo nó tuân theo định dạng đúng. Kiểm định là một công việc xuyên suốt, vì vậy chúng ta nên cố gắng tách nó khỏi business logic của mình.

Đó là lý do tại sao JSR303 và JSR349 cung cấp đặc tả để kiểm định một bean bằng cách sử dụng annotation. Hibernate Validator cung cấp bản triển khai tham chiếu của cả hai đặc tả bean validation này.

45. Lợi ích của Eclipse plugin cho Hibernate Tools là gì?

Hibernate Tools plugin giúp chúng ta viết các file cấu hình và ánh xạ Hibernate một cách dễ dàng hơn. Lợi ích chính là tính năng gợi ý code giúp chúng ta làm việc với các thuộc tính hoặc thẻ XML. Nó cũng xác thực chúng dựa trên các file DTD của Hibernate để giúp chúng ta biết trước các lỗi.

Tổng kết

Tương tự các chủ đề khác, chúng tôi có thể sẽ liên tục cập nhật bổ sung thêm các câu hỏi và trả lời phỏng vấn Hibernate vào danh sách này trong tương lai. Vì vậy, hãy nhớ lưu lại để tham khảo sau khi bạn cần nhé!

0 Bình luận

Đăng nhập để thảo luận

Chuyên mục Hướng dẫn

Tổng hợp các bài viết hướng dẫn, nghiên cứu và phân tích chi tiết về kỹ thuật, các xu hướng công nghệ mới nhất dành cho lập trình viên.

Đăng ký nhận bản tin của chúng tôi

Hãy trở thành người nhận được các nội dung hữu ích của CyStack sớm nhất

Xem chính sách của chúng tôi Chính sách bảo mật.