BAEL-7296: Calling Custom Database Functions with JPA (#15869)

This commit is contained in:
Manfred 2024-02-21 03:31:55 +00:00 committed by GitHub
parent 6328bf349e
commit 1b843fd34a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 190 additions and 0 deletions

View File

@ -62,6 +62,11 @@
<artifactId>modelmapper</artifactId>
<version>${modelmapper.version}</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@ -88,6 +93,7 @@
<hypersistence-utils.version>3.7.0</hypersistence-utils.version>
<jackson.version>2.16.0</jackson.version>
<modelmapper.version>3.2.0</modelmapper.version>
<commons-codec.version>1.16.1</commons-codec.version>
<lombok.version>1.18.30</lombok.version>
</properties>

View File

@ -0,0 +1,13 @@
package com.baeldung.customfunc;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class CustomFunctionApplication {
public static void main(String[] args) {
SpringApplication.run(CustomFunctionApplication.class, args);
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.customfunc;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.query.sqm.function.FunctionKind;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.produce.function.PatternFunctionDescriptorBuilder;
import org.hibernate.type.spi.TypeConfiguration;
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();
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.customfunc;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
import java.util.Map;
@Configuration
public class CustomHibernateConfig implements HibernatePropertiesCustomizer {
@Override
public void customize(Map<String, Object> hibernateProperties) {
hibernateProperties.put("hibernate.dialect", "com.baeldung.customfunc.CustomH2Dialect");
}
}

View File

@ -0,0 +1,31 @@
package com.baeldung.customfunc;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Entity
@Table(name = "product")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = {"id"})
@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;
}

View File

@ -0,0 +1,24 @@
package com.baeldung.customfunc;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.jpa.repository.query.Procedure;
import org.springframework.data.repository.query.Param;
import java.util.List;
public interface ProductRepository extends JpaRepository<Product, Integer> {
@Procedure(name = "Product.sha256Hex")
String getSha256HexByNamedMapping(@Param("value") String value);
@Query(value = "CALL SHA256_HEX(:value)", nativeQuery = true)
String getSha256HexByNativeCall(@Param("value") String value);
@Query(value = "SELECT SHA256_HEX(name) FROM product", nativeQuery = true)
List<String> getProductNameListInSha256HexByNativeSelect();
@Query(value = "SELECT sha256Hex(p.name) FROM Product p")
List<String> getProductNameListInSha256Hex();
}

View File

@ -0,0 +1,58 @@
package com.baeldung.customfunc;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
import java.nio.charset.StandardCharsets;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@SpringBootTest(classes = CustomFunctionApplication.class, properties = {
"spring.jpa.show-sql=true",
"spring.jpa.properties.hibernate.format_sql=true",
"spring.jpa.generate-ddl=true",
"spring.jpa.defer-datasource-initialization=true",
"spring.sql.init.data-locations=classpath:product-data.sql"
})
@Transactional
public class ProductRepositoryIntegrationTest {
private static final String TEXT = "Hand Grip Strengthener";
private static final String EXPECTED_HASH_HEX = getSha256Hex(TEXT);
@Autowired
private ProductRepository productRepository;
private static String getSha256Hex(String value) {
return Hex.encodeHexString(DigestUtils.sha256(value.getBytes(StandardCharsets.UTF_8)));
}
@Test
void whenGetSha256HexByNamedMapping_thenReturnCorrectHash() {
var hash = productRepository.getSha256HexByNamedMapping(TEXT);
assertThat(hash).isEqualTo(EXPECTED_HASH_HEX);
}
@Test
void whenGetSha256HexByNativeCall_thenReturnCorrectHash() {
var hash = productRepository.getSha256HexByNativeCall(TEXT);
assertThat(hash).isEqualTo(EXPECTED_HASH_HEX);
}
@Test
void whenCallGetSha256HexNative_thenReturnCorrectHash() {
var hashList = productRepository.getProductNameListInSha256HexByNativeSelect();
assertThat(hashList.get(0)).isEqualTo(EXPECTED_HASH_HEX);
}
@Test
void whenCallGetSha256Hex_thenReturnCorrectHash() {
var hashList = productRepository.getProductNameListInSha256Hex();
assertThat(hashList.get(0)).isEqualTo(EXPECTED_HASH_HEX);
}
}

View File

@ -0,0 +1,17 @@
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;
}
';
INSERT INTO product(name) VALUES('Hand Grip Strengthener');