Hibernate 筆記

 

Hibernate 筆記

開始之前要有的觀念

Configuration

主要用於啟動、載入和管理 Hibernate的配置文件,創建完 Session 之後聲明週期應該要被拋棄
如果設定檔放在專案的根目錄的話 StandardServiceRegistryBuilder 會自動處理

SessionFactory

用於 Hibernate 的初始化和建立 Session 對象,這個是工廠模式的做法
他是線程安全的,因此可以被多線程使用,但是他是重量級的,因此一般來說一個專案只需要一個 SessionFactory 就好了。
SessionFactory 會做以下幾件事情:

  • 解析設定
  • 建立 connection pool
  • 建立 Mapping
  • 如果有啟用的話會建立Second-Level Cache

session

他會為 Hibernate 提供 DB 的 CRUD 入口,SessionFactory 會提供建立獲取生成 session 的方法。
session 有以下幾個特點:

  • 非線程安全,不可以多線程共用
  • 輕量級,可以多次建立和用完即丟
  • session 有一級緩存

Transaction

交易控制,從 session 的 beginTransaction 取得
有 commit 和 rollback 方法提供 commit 和 rollback

透過 properties 設定

# 連線設定
hibernate.connection.url=jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1
hibernate.connection.username=sa
hibernate.connection.password=

# 印到 console 的設定
hibernate.show_sql=true #直接將 SQL 記錄到控制台
hibernate.format_sql=true #以多行、縮排格式記錄 SQL
hibernate.highlight_sql=true #透過 ANSI 轉義碼以語法高亮方式記錄 SQL

# Automatically export the schema
hibernate.hbm2ddl.auto=create

建立entity

/*
 * SPDX-License-Identifier: Apache-2.0
 * Copyright Red Hat Inc. and Hibernate Authors
 */
package org.hibernate.tutorial.annotations;

import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity //用於宣告說這個 POJO 是 entity
@Table(name = "Events") //宣告是使用於哪個 table,如果沒有寫的話就會是 class name 當作對應的 table
public class Event {

    @Id //將該欄位標記為儲存實體的識別碼(主鍵)
    @GeneratedValue //指定這是一個自動生成的 id,下方有詳細說明
    private Long id;

    private String title;

    @Column(name = "eventDate") //明確指定映射列的名稱。如果沒有此註解會使用變數名稱
    private LocalDateTime date;

    public Event() {
        // this form used by Hibernate
    }

    public Event(String title, LocalDateTime date) {
        // for application use, to create new events
        this.title = title;
        this.date = date;
    }

    public Long getId() {
        return id;
    }

    private void setId(Long id) {
        this.id = id;
    }

    public LocalDateTime getDate() {
        return date;
    }

    public void setDate(LocalDateTime date) {
        this.date = date;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

@GeneratedValue 說明
可以使用 @GeneratedValue(strategy = GenerationType.AUTO) 來設定,其中 GenerationType 是 enum,可以用來選擇不同生成的策略

  • AUTO: 這是預設值,會根據 DB 判斷最適合的策略
  • TABLE: 使用一個特定的 table 來保存主鍵,根據官方資料,此做法效能不好,如果用這個的話需要使用 generator 指定 table
  • SEQUENCE: 根據DB 的序列生成主鍵,前提是 DB 要支援
  • IDENTITY: 由 DB 生成,通常用於自動增長
  • UUID: 生成 UUID 會比較佔空間一點,但好處是與資料庫無關

建立連線

// SessionFactory 在一個應用程式中只會被設定一次!
// setUp() 方法首先會建立一個 StandardServiceRegistry 實例,
// 該實例將設定資訊(來自 hibernate.properties)整合到一組可供 SessionFactory 使用的服務中。
final StandardServiceRegistry registry =
        new StandardServiceRegistryBuilder()
                .build();
try {
    sessionFactory =
            // 1. 使用 StandardServiceRegistry 建立 MetadataSources,
            //    它讓我們可以告訴 Hibernate 我們的領域模型(Domain Model)。
            new MetadataSources(registry) 
                    // 2. 告知 Hibernate 我們要映射的實體類別是 Event.class。
                    .addAnnotatedClass(Event.class) 
                    // 3. 建立 Metadata 物件,它代表了應用程式領域模型的完整、經過部分驗證的視圖。
                    .buildMetadata() 
                    // 4. 最後,建立 SessionFactory。
                    //    SessionFactory 是執行緒安全的,通常在應用程式中只會有一個。
                    .buildSessionFactory(); 
}
catch (Exception e) {
    // 如果建立 SessionFactory 失敗,registry 物件不會被 SessionFactory 銷毀,
    // 所以我們需要手動銷毀它,以釋放資源。
    StandardServiceRegistryBuilder.destroy(registry);
}

透過 Hibernate 提供的方法做 CRUD

public void testBasicUsage() {
    // 做 insert
    sessionFactory.inTransaction(session -> {
        session.persist(new Event("Our very first event!", now()));
        session.persist(new Event("A follow up event", now())); //session.persist 用於 insert
    });

    // 做 update 
    sessionFactory.inTransaction(session -> {
        // 在事務中,Hibernate 會自動偵測到受控物件的變更並將其更新到資料庫。
        // 首先,我們需要載入實體。
        Event eventToUpdate = session.createSelectionQuery("from Event where title = :title", Event.class)
                .setParameter("title", "Our very first event!")
                .getSingleResult();
        // 然後,直接修改物件。
        eventToUpdate.setTitle("Our very first event! (updated)");
    });

    //做安全 delete
    sessionFactory.inTransaction(session -> {
        // 要刪除一個物件,您需要先載入它,然後將它傳遞給 remove() 方法。
        Event eventToDelete = session.createSelectionQuery("from Event where title = :title", Event.class)
                .setParameter("title", "A follow up event")
                .getSingleResult();
        session.remove(eventToDelete);
    });

    //直接 delete
    sessionFactory.inTransaction(session -> {
    int deletedEntities = session.createMutationQuery("delete from Event where title = :title", Event.class)
            .setParameter("title", "A follow up event")
            .executeUpdate();
    out.println("Entities deleted: " + deletedEntities);
    });

    //做 query
    sessionFactory.inTransaction(session -> {
        session.createSelectionQuery("from Event", Event.class).getResultList()
                .forEach(event -> out.println("Event (" + event.getDate() + ") : " + event.getTitle()));
    });
    
    //帶搜尋條件的搜尋,用問號
    sessionFactory.inSession(session -> {
        var hql = "from Event where title = ? and id = ?";
        var query = session.createQuery(hql, Event.class);
        query.setParameter(0, "A follow up event");
        query.setParameter(1, 2L);
        var result = query.getResultList();
        out.println(result);
    });

    //帶搜尋條件的搜尋,用變數
    sessionFactory.inSession(session -> {
        var hql = "from Event where title = :title"; //設定 hql,
        var query = session.createQuery(hql, Event.class); //透過 session createQuery
        query.setParameter("title", "A follow up event"); //
        var result = query.getResultList();
        out.println(result);
    });
    
    //聚合函數
    sessionFactory.inSession(session -> {
        var hql = "select count(id) from Event";
        var query = session.createQuery(hql, Long.class);
        out.println(query.getSingleResult());
    });
    
    //分組
    sessionFactory.inSession(session -> {
        var hql = "select e.title, count(e.id) from Event e group by e.title";
        var query = session.createQuery(hql, Object[].class);
        out.println(query.getResultList());
    });
}

HQL 語句

HQL 語句說明
count同 SQL
sum同 SQL
avg同 SQL
max同 SQL
min同 SQL

留言

這個網誌中的熱門文章

基本 Spring security 快速入門

記帳專案說明

JMSTemplate 的教學