知識庫 / JPA RSS 訂閱

使用 JPA 和 Spring Boot 調用自定義數據庫函數

JPA,Spring Boot
HongKong
11
11:17 AM · Dec 06 ,2025

1. 概述

數據庫函數是數據庫管理系統中的關鍵組件,它們能夠封裝邏輯並將其執行限制在數據庫內部。 它們促進了高效的數據處理和操作。

在本教程中,我們將探討在 JPA 和 Spring Boot 應用程序中調用自定義數據庫函數的方法。

2. 項目設置

我們將後續章節中演示的概念使用 H2 數據庫。

讓我們在我們的 pom.xml 中包含 Spring Boot Data JPAH2 依賴項:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>3.2.2</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.2.224</version>
</dependency>

3. 數據庫函數

數據庫函數是數據庫對象,通過在數據庫中執行一組 SQL 語句或操作來執行特定任務。這在邏輯具有大量數據時可以提高性能。雖然數據庫函數和存儲過程在操作方式上相似,但它們之間存在差異。

3.1 函數與存儲過程

雖然不同的數據庫系統之間可能存在特定差異,但它們的主要區別可以總結如下表:

特性 數據庫函數 存儲過程
調用方式 可在查詢中調用 必須顯式調用
返回值 始終返回單個值 可能返回無、單個或多個值
參數 僅支持輸入參數 支持輸入和輸出參數
調用 不能使用函數調用存儲過程 可以使用存儲過程調用函數
用途 通常用於執行計算或數據轉換 常用於複雜的業務邏輯

3.2. H2 函數

為了説明如何從 JPA 調用數據庫函數,我們將創建一個 H2 數據庫函數,以演示如何從 JPA 調用它。H2 數據庫函數實際上是嵌入的 Java 源代碼,它將被編譯並執行。

CREATE ALIAS SHA256_HEX AS '
    import java.sql.*;
    @CODE
    String getSha256Hex(Connection conn, String value) throws SQLException {
        var sql = "SELECT RAWTOHEX(HASH(''SHA-256'', ?))";
        try (PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setString(1, value);
            ResultSet rs = stmt.executeQuery();
            if (rs.next()) {
                return rs.getString(1);
            }
        }
        return null;
    }
';

此數據庫函數 SHA256_HEX 接受一個字符串作為輸入參數,通過 SHA-256 散列算法進行處理,並隨後返回其 SHA-256 散列的十六進制表示。

4. 調用存儲過程

第一種方法是類似於存儲過程的數據庫函數在 JPA 中進行調用。 我們通過在實體類中添加 @NamedStoredProcedureQuery 註解來實現的。 此註解允許我們在實體類中直接指定存儲過程的元數據。

以下是包含定義的存儲過程 SHA256_HEXProduct 實體類示例:

@Entity
@Table(name = "product")
@NamedStoredProcedureQuery(
  name = "Product.sha256Hex",
  procedureName = "SHA256_HEX",
  parameters = @StoredProcedureParameter(mode = ParameterMode.IN, name = "value", type = String.class)
)
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "product_id")
    private Integer id;

    private String name;

    // constructor, getters and setters
}

在實體類中,我們使用 Product 實體類並對其進行@NamedStoredProcedureQuery 註解。我們將 Product.sha256Hex 設置為命名存儲過程的名稱。

在我們的存儲庫定義中,我們使用@Procedure 註解存儲庫方法,並引用我們@NamedStoredProcedureQuery 的名稱。此存儲庫方法接受一個字符串參數,然後將其傳遞給數據庫函數,並返回數據庫函數調用的結果。

public interface ProductRepository extends JpaRepository<Product, Integer> {
    @Procedure(name = "Product.sha256Hex")
    String getSha256HexByNamed(@Param("value") String value);
}

我們將會看到 Hibernate 像調用存儲過程一樣,在 Hibernate 日誌中執行時調用它:

Hibernate: 
    {call SHA256_HEX(?)}

是主要用於調用存儲過程。 數據庫函數可以單獨調用,類似於存儲過程。但是,它可能不適合用於與 SELECT 查詢一起使用的數據庫函數。

5. 原生查詢

通過原生查詢調用數據庫函數的一種方法是使用原生查詢。 使用原生查詢調用數據庫函數有兩的不同方式。

5.1. 本地調用

從之前的 Hibernate 日誌中,我們可以看到 Hibernate 執行了一個 CALL 命令。同樣,我們可以使用相同的命令來調用我們的數據庫函數:

public interface ProductRepository extends JpaRepository<Product, Integer> {
    @Query(value = "CALL SHA256_HEX(:value)", nativeQuery = true)
    String getSha256HexByNativeCall(@Param("value") String value);
}

執行結果將與我們使用 @NamedStoredProcedureQuery 的示例相同。

5.2. 原生選擇

如前所述,我們無法將其與選擇查詢結合使用。我們將將其轉換為選擇查詢,並將其應用於表中列的值。在我們的示例中,我們定義了一個倉庫方法,它使用原生選擇查詢來調用數據庫函數,該函數應用於 name 列(即 Product 表)的值:

public interface ProductRepository extends JpaRepository<Product, Integer> {
    @Query(value = "SELECT SHA256_HEX(name) FROM product", nativeQuery = true)
    String getProductNameListInSha256HexByNativeSelect();
}

在執行後,我們可以獲得與我們定義的完全相同的查詢,因為我們將其定義為原生查詢。

Hibernate: 
    SELECT
        SHA256_HEX(name) 
    FROM
        product

6. 函數註冊

函數註冊是 Hibernate 中定義和註冊自定義數據庫函數的過程,這些函數可用於 JPA 或 Hibernate 查詢中。這有助於 Hibernate 將自定義函數翻譯成相應的 SQL 語句。

6.1. 自定義方言

通過創建自定義方言,我們可以註冊自定義函數。 以下是擴展默認的 H2Dialect 類並註冊我們自定義函數的自定義方言類:

public class CustomH2Dialect extends H2Dialect {
    @Override
    public void initializeFunctionRegistry(FunctionContributions functionContributions) {
        super.initializeFunctionRegistry(functionContributions);
        SqmFunctionRegistry registry = functionContributions.getFunctionRegistry();
        TypeConfiguration types = functionContributions.getTypeConfiguration();

        new PatternFunctionDescriptorBuilder(registry, "sha256hex", FunctionKind.NORMAL, "SHA256_HEX(?1)")
          .setExactArgumentCount(1)
          .setInvariantType(types.getBasicTypeForJavaType(String.class))
          .register();
    }
}

當 Hibernate 初始化一個方言時,它會通過 initializeFunctionRegistry() 方法將可用的數據庫函數註冊到函數註冊表中。 我們通過覆蓋 initializeFunctionRegistry() 方法來註冊默認方言未包含的額外數據庫函數。

PatternFunctionDescriptorBuilder 創建一個 JPQL 函數映射,將我們的數據庫函數 SHA256_HEX 映射到 JPQL 函數 sha256Hex,並將映射註冊到函數註冊表中。參數 ?1 表示數據庫函數的第一個輸入參數。

6.2. Hibernate 配置

我們需要指示 Spring Boot 採用 CustomH2Dialect 而不是默認的 H2Dialect。這裏使用了 HibernatePropertiesCustomizer,這是一個 Spring Boot 提供的接口,用於自定義 Hibernate 使用的屬性。

我們通過覆蓋 customize() 方法來添加額外的屬性,表明我們將使用 CustomH2Dialect

@Configuration
public class CustomHibernateConfig implements HibernatePropertiesCustomizer {
    @Override
    public void customize(Map<String, Object> hibernateProperties) {
        hibernateProperties.put("hibernate.dialect", "com.baeldung.customfunc.CustomH2Dialect");
    }
}

Spring Boot 在應用程序啓動時會自動檢測並應用自定義配置。

6.3. 倉庫方法

在我們的倉庫中,現在可以使用將新定義的函數 sha256Hex 替代原生查詢的 JPQL 查詢:

public interface ProductRepository extends JpaRepository<Product, Integer> {
    @Query(value = "SELECT sha256Hex(p.name) FROM Product p")
    List<String> getProductNameListInSha256Hex();
}

當我們執行後檢查Hibernate日誌時,我們看到Hibernate正確地將JPQL的sha256Hex函數翻譯為我們數據庫中的函數SHA256_HEX

Hibernate: 
    select
        SHA256_HEX(p1_0.name) 
    from
        product p1_07'

7. 結論

在本文中,我們對數據庫函數和存儲過程進行了簡要比較。兩者都提供了一種強大的方式來封裝邏輯並執行在數據庫中。

此外,我們還探討了調用數據庫函數的不同方法,包括利用 <em @NamedStoredProcedureQuery</em> 註解、原生查詢以及通過自定義方言註冊函數。通過將數據庫函數整合到存儲庫方法中,我們可以輕鬆構建數據庫驅動的應用程序。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.