Hibernate Criteria Query Language (HCQL)

Bài viết được sự cho phép của tác giả Giang Phan

Trong bài trước, chúng ta đã cùng tìm hiểu về cách sử dụng Hibernate Query Language (HQL) để truy vấn cơ sở dữ liệu và nhận kết quả. Trong bài này, chúng ta sẽ cùng tìm hiểu về Hibernate Criteria Query Language – một cách khác để lấy dữ liệu dựa vào các tiêu chí (Criteria) cụ thể.

  Hibernate Interceptor & StatementInspector
  Hibernate Batch processing

Giới hiệu Hibernate Criteria Query Language (HCQL)

Hibernate Criteria API cung cấp cách tiếp cận hướng đối tượng để truy vấn cơ sở dữ liệu và nhận kết quả. Criteria cho phép chúng ta xây dựng câu lệnh một cách linh động (dynamic) và không bị hardcode trong một chuỗi truy vấn và có thể tái sử dụng.

Một số trường hợp sử dụng phổ biến của Hibernate Criteria Query:

  • Sử dụng các hàm tập hợp như sum(), min(), max(), …
  • Truy vấn một vài column cụ thể.
  • Sử dụng để join nhiều table với nhau.
  • Giới hạn dữ liệu được trả về.
  • Sắp xếp (Order) kết quả trả về.
  • Phân trang (Paging).

Tương tự như HQL, để tạo đối tượng Criteria chúng ta sẽ sử dụng phương thức getCriteriaBuilder() từ đối tượng Session và tạo đối tượng Criteria: CriteriaQuery, createCriteriaUpdate, createCriteriaDelete.

1
2
3
4
5
<T> javax.persistence.criteria.CriteriaQuery<T> createQuery(java.lang.Class<T> aClass);
<T> javax.persistence.criteria.CriteriaUpdate<T> createCriteriaUpdate(java.lang.Class<T> aClass);
<T> javax.persistence.criteria.CriteriaDelete<T> createCriteriaDelete(java.lang.Class<T> aClass);

Ví dụ sử dụng Hibernate Criteria Query Language (HCQL)

Chúng ta sẽ sử dụng lại cơ sở dữ liệu ở bài viết trước để thực hiện các truy vấn với Criteria.

Lấy 1 đối tượng

Ví dụ lấy user có id là 1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try (Session session = HibernateUtils.getSessionFactory().openSession();) {
    // Begin a unit of work
    session.beginTransaction();
    CriteriaBuilder builder = session.getCriteriaBuilder();
    CriteriaQuery<User> query = builder.createQuery(User.class);
    Root<User> root = query.from(User.class); // FROM User u
    query.select(root); // SELECT
    query.where(builder.equal(root.get("id"), 1)); // WHERE u.id = 1
    User user = session.createQuery(query).uniqueResult();
    // Commit the current resource transaction, writing any unflushed changes to the database.
    session.getTransaction().commit();
}

Lấy danh sách với paging và ordering

Ví dụ lấy danh sách user có id >= 1 và id <= 1000. Sắp xếp danh sách này theo ngày tạo giảm dần, tên tăng dần. Lấy từ 5 user bắt đầu từ vị trí thứ 10.

1
2
3
4
5
6
7
8
9
10
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> root = query.from(User.class); // FROM
query.select(root); // SELECT
query.where(builder.and(builder.ge(root.get("id"), 1), builder.le(root.get("id"), 1000))); // WHERE id >= 1 AND id <= 1000
query.orderBy(builder.desc(root.get("createdAt")), builder.desc(root.asc("fullname"))); // ORDER BY createdAt DESC, fullname ASC
List<User> users = session.createQuery(query)
        .setFirstResult(10)
        .setMaxResults(5)
        .getResultList();

Lấy một column (Selecting an expression)

Ví dụ lấy tất cả email của user.

1
2
3
4
5
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<String> query = builder.createQuery(String.class);
Root<User> root = query.from(User.class); // FROM
query.select(root.get("fullname")); // SELECT fullname
List<String> fullnames = session.createQuery(query).getResultList();

Lấy nhiều column (Selecting multiple values)

1
2
3
4
5
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Object[]> query = builder.createQuery(Object[].class);
Root<User> root = query.from(User.class); // FROM
query.multiselect(root.get("fullname"), root.get("username")); // SELECT fullname, username
List<Object[]> users = session.createQuery(query).getResultList();

Lấy nhiều column sử dụng Wrapper (Selecting a wrapper)

Thay vì trả về list Object[], chúng ta có thể trả về List POJO class như sau:

  • Tạo class mới, chứa các cột trả về.
  • Sử dụng phương thức construct của CriteriaBuilder để gán giá trị tương ứng cho wrapper class.

Chẳng hạn, cần lấy 2 column là fullname và username

1
2
3
4
5
6
@Data
@AllArgsConstructor
class BaseUser {
    private String fullname;
    private String username;
}
1
2
3
4
5
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<BaseUser> query = builder.createQuery(BaseUser.class);
Root<User> root = query.from(User.class); // FROM
query.select( builder.construct( BaseUser.class, root.get("fullname"), root.get("username") ) ); // SELECT fullname, username
List<BaseUser> users = session.createQuery(query).getResultList();

Sử dụng hàm tập hợp (Aggregate Functions)

Ví dụ đếm số lượng user được tạo theo tháng.

1
2
3
4
5
6
7
8
9
10
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Object[]> query = builder.createQuery(Object[].class);
Root<User> root = query.from(User.class);
Expression<Long> groupByExp = builder.function("month", Long.class, root.get("createdAt")).as(Long.class);
Expression<Long> countExp = builder.count(root.get("id"));
query.multiselect(groupByExp, countExp);
query.groupBy(groupByExp);
query.having(builder.gt(builder.count(root), 3));
//ordering by count in descending order
query.orderBy(builder.desc(countExp));

Truy vấn nhiều bảng (join)

Ví dụ: lấy thông tin user và user profile.

1
2
3
4
5
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<User> query = builder.createQuery(User.class);
Root<User> root = query.from(User.class); // FROM
Join<User, UserProfile> userJoin = root.join("userProfile", JoinType.LEFT);
List<User> users = session.createQuery(query).getResultList();

Một số bất lợi khi sử dụng của Criteria

Như bạn thấy, Query với Criteria khá đơn giản. Tuy nhiên, nó có một số vấn đề sau chúng ta cần xem xét trước khi sử dụng:

  • Performance issue: Chúng ta không có cách nào để kiểm soát truy vấn SQL do Hibernate tạo ra, nếu truy vấn được tạo chậm, ta rất khó điều chỉnh truy vấn.
  • Maintenace issue: Tất cả các truy vấn SQL được phân tán thông qua mã code Java, khi một truy vấn bị lỗi, có thể dành thời gian để tìm truy vấn gây ra vấn đề trong ứng dụng của mình.

Không có gì là hoàn hảo, hãy xem xét nhu cầu dự án của mình và sử dụng nó một cách phù hợp. Đó cũng là một trong những lý dó mà Hibernate support nhiều loại truy vấn khác nhau.

Tài liệu tham khảo:

Bài viết gốc được đăng tải tại gpcoder.com

Có thể bạn quan tâm:

Xem thêm Việc làm IT hấp dẫn trên TopDev