fix problem with Postgres function array return value in Hibernate 6
This commit is contained in:
parent
063fcf90a3
commit
ec53934f30
@ -60,9 +60,10 @@ dependencies {
|
|||||||
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
implementation 'org.springframework.boot:spring-boot-starter-validation'
|
||||||
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.1'
|
implementation 'com.github.gavlyukovskiy:datasource-proxy-spring-boot-starter:1.9.1'
|
||||||
implementation 'org.springdoc:springdoc-openapi:2.3.0'
|
implementation 'org.springdoc:springdoc-openapi:2.3.0'
|
||||||
|
implementation 'org.postgresql:postgresql:42.7.1'
|
||||||
implementation 'org.liquibase:liquibase-core:4.25.1'
|
implementation 'org.liquibase:liquibase-core:4.25.1'
|
||||||
implementation 'com.vladmihalcea:hibernate-types-60:2.21.1'
|
implementation 'com.vladmihalcea:hibernate-types-60:2.21.1'
|
||||||
implementation 'io.hypersistence:hypersistence-utils-hibernate-64:3.7.0'
|
implementation 'io.hypersistence:hypersistence-utils-hibernate-62:3.7.0'
|
||||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1'
|
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.16.1'
|
||||||
implementation 'org.openapitools:jackson-databind-nullable:0.2.6'
|
implementation 'org.openapitools:jackson-databind-nullable:0.2.6'
|
||||||
implementation 'org.apache.commons:commons-text:1.11.0'
|
implementation 'org.apache.commons:commons-text:1.11.0'
|
||||||
@ -75,7 +76,6 @@ dependencies {
|
|||||||
|
|
||||||
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
developmentOnly 'org.springframework.boot:spring-boot-devtools'
|
||||||
|
|
||||||
runtimeOnly 'org.postgresql:postgresql:42.7.1'
|
|
||||||
|
|
||||||
annotationProcessor 'org.projectlombok:lombok'
|
annotationProcessor 'org.projectlombok:lombok'
|
||||||
testAnnotationProcessor 'org.projectlombok:lombok'
|
testAnnotationProcessor 'org.projectlombok:lombok'
|
||||||
@ -214,7 +214,7 @@ project.tasks.check.dependsOn(checkLicense)
|
|||||||
|
|
||||||
// JaCoCo Test Code Coverage
|
// JaCoCo Test Code Coverage
|
||||||
jacoco {
|
jacoco {
|
||||||
toolVersion = "0.8.8"
|
toolVersion = "0.8.10"
|
||||||
}
|
}
|
||||||
test {
|
test {
|
||||||
finalizedBy jacocoTestReport // generate report after tests
|
finalizedBy jacocoTestReport // generate report after tests
|
||||||
|
@ -8,7 +8,7 @@ import static org.hibernate.dialect.DatabaseVersion.make;
|
|||||||
public class PostgresCustomDialect extends PostgreSQLDialect {
|
public class PostgresCustomDialect extends PostgreSQLDialect {
|
||||||
|
|
||||||
public PostgresCustomDialect() {
|
public PostgresCustomDialect() {
|
||||||
super(make(13, 7));
|
super(make(15, 5));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,11 @@ import java.util.Collections;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static java.util.function.Predicate.not;
|
import static java.util.function.Predicate.not;
|
||||||
|
import static net.hostsharing.hsadminng.mapper.PostgresArray.fromPostgresArray;
|
||||||
import static org.springframework.transaction.annotation.Propagation.MANDATORY;
|
import static org.springframework.transaction.annotation.Propagation.MANDATORY;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@ -81,11 +83,14 @@ public class Context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String[] getAssumedRoles() {
|
public String[] getAssumedRoles() {
|
||||||
return (String[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult();
|
final byte[] result = (byte[]) em.createNativeQuery("select assumedRoles() as roles", String[].class).getSingleResult();
|
||||||
|
return fromPostgresArray(result, String.class, Function.identity());
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID[] currentSubjectsUuids() {
|
public UUID[] currentSubjectsUuids() {
|
||||||
return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
|
final byte[] result = (byte[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class)
|
||||||
|
.getSingleResult();
|
||||||
|
return fromPostgresArray(result, UUID.class, UUID::fromString);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getCallerMethodNameFromStackFrame(final int skipFrames) {
|
public static String getCallerMethodNameFromStackFrame(final int skipFrames) {
|
||||||
|
@ -47,7 +47,7 @@ public class HsOfficeCoopAssetsTransactionEntity implements Stringifyable {
|
|||||||
|
|
||||||
@Column(name = "transactiontype")
|
@Column(name = "transactiontype")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Type(PostgreSQLEnumType.class)
|
//@Type(PostgreSQLEnumType.class)
|
||||||
private HsOfficeCoopAssetsTransactionType transactionType;
|
private HsOfficeCoopAssetsTransactionType transactionType;
|
||||||
|
|
||||||
@Column(name = "valuedate")
|
@Column(name = "valuedate")
|
||||||
|
@ -43,7 +43,7 @@ public class HsOfficeCoopSharesTransactionEntity implements Stringifyable {
|
|||||||
|
|
||||||
@Column(name = "transactiontype")
|
@Column(name = "transactiontype")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Type(PostgreSQLEnumType.class)
|
//@Type(PostgreSQLEnumType.class)
|
||||||
private HsOfficeCoopSharesTransactionType transactionType;
|
private HsOfficeCoopSharesTransactionType transactionType;
|
||||||
|
|
||||||
@Column(name = "valuedate")
|
@Column(name = "valuedate")
|
||||||
|
@ -19,9 +19,9 @@ public interface HsOfficeDebitorRepository extends Repository<HsOfficeDebitorEnt
|
|||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT debitor FROM HsOfficeDebitorEntity debitor
|
SELECT debitor FROM HsOfficeDebitorEntity debitor
|
||||||
JOIN HsOfficePartnerEntity partner ON partner.uuid = debitor.partner
|
JOIN HsOfficePartnerEntity partner ON partner.uuid = debitor.partner.uuid
|
||||||
JOIN HsOfficePersonEntity person ON person.uuid = partner.person
|
JOIN HsOfficePersonEntity person ON person.uuid = partner.person.uuid
|
||||||
JOIN HsOfficeContactEntity contact ON contact.uuid = debitor.billingContact
|
JOIN HsOfficeContactEntity contact ON contact.uuid = debitor.billingContact.uuid
|
||||||
WHERE :name is null
|
WHERE :name is null
|
||||||
OR partner.details.birthName like concat(:name, '%')
|
OR partner.details.birthName like concat(:name, '%')
|
||||||
OR person.tradeName like concat(:name, '%')
|
OR person.tradeName like concat(:name, '%')
|
||||||
|
@ -61,7 +61,7 @@ public class HsOfficeMembershipEntity implements Stringifyable {
|
|||||||
|
|
||||||
@Column(name = "reasonfortermination")
|
@Column(name = "reasonfortermination")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Type(PostgreSQLEnumType.class)
|
//@Type(PostgreSQLEnumType.class)
|
||||||
private HsOfficeReasonForTermination reasonForTermination;
|
private HsOfficeReasonForTermination reasonForTermination;
|
||||||
|
|
||||||
public void setValidFrom(final LocalDate validFrom) {
|
public void setValidFrom(final LocalDate validFrom) {
|
||||||
|
@ -13,8 +13,8 @@ public interface HsOfficePartnerRepository extends Repository<HsOfficePartnerEnt
|
|||||||
|
|
||||||
@Query("""
|
@Query("""
|
||||||
SELECT partner FROM HsOfficePartnerEntity partner
|
SELECT partner FROM HsOfficePartnerEntity partner
|
||||||
JOIN HsOfficeContactEntity contact ON contact.uuid = partner.contact
|
JOIN HsOfficeContactEntity contact ON contact.uuid = partner.contact.uuid
|
||||||
JOIN HsOfficePersonEntity person ON person.uuid = partner.person
|
JOIN HsOfficePersonEntity person ON person.uuid = partner.person.uuid
|
||||||
WHERE :name is null
|
WHERE :name is null
|
||||||
OR partner.details.birthName like concat(:name, '%')
|
OR partner.details.birthName like concat(:name, '%')
|
||||||
OR contact.label like concat(:name, '%')
|
OR contact.label like concat(:name, '%')
|
||||||
|
@ -37,7 +37,7 @@ public class HsOfficePersonEntity implements Stringifyable {
|
|||||||
|
|
||||||
@Column(name = "persontype")
|
@Column(name = "persontype")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Type(PostgreSQLEnumType.class)
|
//@Type(PostgreSQLEnumType.class)
|
||||||
private HsOfficePersonType personType;
|
private HsOfficePersonType personType;
|
||||||
|
|
||||||
@Column(name = "tradename")
|
@Column(name = "tradename")
|
||||||
|
@ -47,7 +47,7 @@ public class HsOfficeRelationshipEntity {
|
|||||||
|
|
||||||
@Column(name = "reltype")
|
@Column(name = "reltype")
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@Type(PostgreSQLEnumType.class)
|
//@Type(PostgreSQLEnumType.class)
|
||||||
private HsOfficeRelationshipType relType;
|
private HsOfficeRelationshipType relType;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package net.hostsharing.hsadminng.mapper;
|
||||||
|
|
||||||
|
import com.vladmihalcea.hibernate.type.range.Range;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
import org.postgresql.util.PGtokenizer;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
@UtilityClass
|
||||||
|
public class PostgresArray {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a byte[], as returned for a Postgres-array by native queries, to a Java array.
|
||||||
|
*
|
||||||
|
* <p>This example code worked with Hibernate 5 (Spring Boot 3.0.x):
|
||||||
|
* <pre><code>
|
||||||
|
* return (UUID[]) em.createNativeQuery("select currentSubjectsUuids() as uuids", UUID[].class).getSingleResult();
|
||||||
|
* </code></pre>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>With Hibernate 6 (Spring Boot 3.1.x), this utility method can be used like such:
|
||||||
|
* <pre><code>
|
||||||
|
* final byte[] result = (byte[]) em.createNativeQuery("select * from currentSubjectsUuids() as uuids", UUID[].class)
|
||||||
|
* .getSingleResult();
|
||||||
|
* return fromPostgresArray(result, UUID.class, UUID::fromString);
|
||||||
|
* </code></pre>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param pgArray the byte[] returned by a native query containing as rendered for a Postgres array
|
||||||
|
* @param elementClass the class of a single element of the Java array to be returned
|
||||||
|
* @param itemParser converts a string element to the specified elementClass
|
||||||
|
* @return a Java array containing the data from pgArray
|
||||||
|
* @param <T> type of a single element of the Java array
|
||||||
|
*/
|
||||||
|
public static <T> T[] fromPostgresArray(final byte[] pgArray, final Class<T> elementClass, final Function<String, T> itemParser) {
|
||||||
|
final var pgArrayLiteral = new String(pgArray, StandardCharsets.UTF_8);
|
||||||
|
if (pgArrayLiteral.length() == 2) {
|
||||||
|
return newGenericArray(elementClass, 0);
|
||||||
|
}
|
||||||
|
final PGtokenizer tokenizer = new PGtokenizer(pgArrayLiteral.substring(1, pgArrayLiteral.length()-1), ',');
|
||||||
|
tokenizer.remove("\"", "\"");
|
||||||
|
final T[] array = newGenericArray(elementClass, tokenizer.getSize()); // Create a new array of the specified type and length
|
||||||
|
for ( int n = 0; n < tokenizer.getSize(); ++n ) {
|
||||||
|
array[n] = itemParser.apply(tokenizer.getToken(n).trim().replace("\\\"", "\""));
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> T[] newGenericArray(final Class<T> elementClass, final int length) {
|
||||||
|
return (T[]) Array.newInstance(elementClass, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user