Spring Boot中使用JPA呼叫自定義的資料庫函式

banq發表於2024-02-25

資料庫函式是資料庫管理系統中的重要元件, 將邏輯和執行封裝在資料庫中。它們促進高效的資料處理和操作。

依賴:
讓我們在pom.xml中包含Spring Boot Data JPA和H2依賴項:

<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>

資料庫函式
資料庫函式是透過在資料庫中執行一組 SQL 語句或操作來執行特定任務的資料庫物件。當邏輯是資料密集型時,這可以提高效能。儘管資料庫函式和儲存過程的操作類似,但它們卻存在差異。

資料庫函式 vs. 儲存過程
雖然不同的資料庫系統之間可能存在特定的差異,但它們之間的主要差異可以:

  • 資料庫函式通常執行計算或資料轉換    
  • 儲存過程通常用於複雜的業務邏輯

資料庫函式案例
為了說明從 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 雜湊的十六進位制表示形式。

作為儲存過程呼叫
第一種方法是呼叫類似於 JPA 中的儲存過程的資料庫函式。我們透過@NamedStoredProcedureQuery註解實體類來完成它。該註釋允許我們直接在實體類中指定儲存過程的後設資料。

以下是具有定義的儲存過程SHA256_HEX的Product實體類的示例:

@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
}

在實體類中,我們使用@NamedStoredProcedureQuery註釋我們的Product實體類。我們將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(?)}

@NamedStoredProcedureQuery主要用於呼叫儲存過程。資料庫函式可以單獨呼叫,也類似於儲存過程。但是,對於與選擇查詢結合使用的資料庫函式來說,它可能並不理想。

原生查詢
呼叫資料庫函式的另一種方法是透過本機查詢。使用本機查詢呼叫資料庫函式有兩種不同的方法。

本地呼叫
從前面的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 的示例中看到的相同。

本機選擇
正如我們之前所描述的,我們不能將它與選擇查詢結合使用。我們將其切換為選擇查詢並將該函式應用於表中的列值。在我們的示例中,我們定義了一個儲存庫方法,該方法使用本機選擇查詢來呼叫Product表的名稱列上的資料庫函式:

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

執行後,我們可以從 Hibernate 日誌中獲取與我們定義相同的查詢,因為我們將其定義為本機查詢:

Hibernate: 
    SELECT
        SHA256_HEX(name) 
    FROM
        product

函式註冊
函式註冊是定義和註冊可在 JPA 或 Hibernate 查詢中使用的自定義資料庫函式的 Hibernate 過程。這有助於 Hibernate 將自定義函式翻譯成相應的 SQL 語句。

自定義方言
我們可以透過建立自定義方言來註冊自定義函式。這是擴充套件預設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指示資料庫函式的第一個輸入引數。

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 在應用程式啟動期間自動檢測並應用自定義。

儲存庫方法
在我們的儲存庫中,我們現在可以使用應用新定義的函式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'

結論
在本文中,我們對資料庫函式和儲存過程進行了簡要比較。兩者都提供了封裝邏輯並在資料庫中執行的強大方法。

此外,我們還探索了呼叫資料庫函式的不同方法,包括利用@NamedStoredProcedureQuery註釋、本機查詢和透過自定義方言進行函式註冊。透過將資料庫功能合併到儲存庫方法中,我們可以輕鬆構建資料庫驅動的應用程式。
 

相關文章