diff --git a/pom.xml b/pom.xml
index a40fa7b7..84ded526 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,8 +23,8 @@
17
17
UTF-8
- 3.0.0
- 2022.0.0
+ 3.4.1
+ 2024.1.1
4.1.4
2.3.2
1.18.24
diff --git a/rsql-common/pom.xml b/rsql-common/pom.xml
index 4424ab81..a1c347e5 100644
--- a/rsql-common/pom.xml
+++ b/rsql-common/pom.xml
@@ -34,8 +34,8 @@
io.hypersistence
- hypersistence-utils-hibernate-62
- 3.5.1
+ hypersistence-utils-hibernate-63
+ 3.9.0
test
diff --git a/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java b/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java
index 8fe8eda6..2a06df1d 100644
--- a/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java
+++ b/rsql-common/src/main/java/io/github/perplexhub/rsql/RSQLVisitorBase.java
@@ -1,6 +1,7 @@
package io.github.perplexhub.rsql;
import java.lang.reflect.*;
+import java.sql.Timestamp;
import java.time.*;
import java.time.format.DateTimeParseException;
import java.util.*;
@@ -13,6 +14,7 @@
import jakarta.persistence.metamodel.PluralAttribute;
import lombok.Getter;
+import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.orm.jpa.vendor.Database;
@@ -52,6 +54,15 @@ public static Database getDatabase(EntityManager entityManager) {
return entityManagerDatabase.get(entityManager);
}
+ public static Attribute super T, ?> getAttribute(String property, ManagedType classMetadata) {
+ // W/A found here: https://hibernate.atlassian.net/browse/HHH-18569
+ // breaking change on hibernate side: https://github.com/hibernate/hibernate-orm/pull/6924#discussion_r1250474422
+ if (classMetadata instanceof ManagedDomainType managedDomainType) {
+ return managedDomainType.findSubTypesAttribute(property);
+ }
+ return classMetadata.getAttribute(property);
+ }
+
protected abstract Map getPropertyPathMapper();
public Map, Map> getPropertyRemapping() {
@@ -71,6 +82,9 @@ protected Object convert(String source, Class targetType) {
object = UUID.fromString(source);
} else if (targetType.equals(Date.class) || targetType.equals(java.sql.Date.class)) {
object = java.sql.Date.valueOf(LocalDate.parse(source));
+ } else if (targetType.equals(Timestamp.class)) {
+ Date date = java.sql.Date.valueOf(LocalDate.parse(source));
+ return new Timestamp(date.getTime());
} else if (targetType.equals(LocalDate.class)) {
object = LocalDate.parse(source);
} else if (targetType.equals(LocalDateTime.class)) {
@@ -158,10 +172,10 @@ protected String mapProperty(String selector, Class> entityClass) {
protected Class> findPropertyType(String property, ManagedType classMetadata) {
Class> propertyType = null;
- if (classMetadata.getAttribute(property).isCollection()) {
- propertyType = ((PluralAttribute) classMetadata.getAttribute(property)).getBindableJavaType();
+ if (getAttribute(property, classMetadata).isCollection()) {
+ propertyType = ((PluralAttribute) getAttribute(property, classMetadata)).getBindableJavaType();
} else {
- propertyType = classMetadata.getAttribute(property).getJavaType();
+ propertyType = getAttribute(property, classMetadata).getJavaType();
}
return propertyType;
}
@@ -217,7 +231,7 @@ protected ManagedType getManagedElementCollectionType(String mappedProper
protected boolean hasPropertyName(String property, ManagedType classMetadata) {
try {
- return classMetadata.getAttribute(property) != null;
+ return getAttribute(property, classMetadata) != null;
} catch (IllegalArgumentException e) {
return false;
}
@@ -238,30 +252,30 @@ protected static Class getElementCollectionGenericType(Class type, Attribute att
}
protected boolean isEmbeddedType(String property, ManagedType classMetadata) {
- return classMetadata.getAttribute(property).getPersistentAttributeType() == PersistentAttributeType.EMBEDDED;
+ return getAttribute(property, classMetadata).getPersistentAttributeType() == PersistentAttributeType.EMBEDDED;
}
protected boolean isElementCollectionType(String property, ManagedType classMetadata) {
- return classMetadata.getAttribute(property).getPersistentAttributeType() == PersistentAttributeType.ELEMENT_COLLECTION;
+ return getAttribute(property, classMetadata).getPersistentAttributeType() == PersistentAttributeType.ELEMENT_COLLECTION;
}
protected boolean isAssociationType(String property, ManagedType classMetadata) {
- return classMetadata.getAttribute(property).isAssociation();
+ return getAttribute(property, classMetadata).isAssociation();
}
protected boolean isOneToOneAssociationType(String property, ManagedType classMetadata) {
- return classMetadata.getAttribute(property).isAssociation()
- && PersistentAttributeType.ONE_TO_ONE == classMetadata.getAttribute(property).getPersistentAttributeType();
+ return getAttribute(property, classMetadata).isAssociation()
+ && PersistentAttributeType.ONE_TO_ONE == getAttribute(property, classMetadata).getPersistentAttributeType();
}
protected boolean isOneToManyAssociationType(String property, ManagedType classMetadata) {
- return classMetadata.getAttribute(property).isAssociation()
- && PersistentAttributeType.ONE_TO_MANY == classMetadata.getAttribute(property).getPersistentAttributeType();
+ return getAttribute(property, classMetadata).isAssociation()
+ && PersistentAttributeType.ONE_TO_MANY == getAttribute(property, classMetadata).getPersistentAttributeType();
}
protected boolean isManyToManyAssociationType(String property, ManagedType classMetadata) {
- return classMetadata.getAttribute(property).isAssociation()
- && PersistentAttributeType.MANY_TO_MANY == classMetadata.getAttribute(property).getPersistentAttributeType();
+ return getAttribute(property, classMetadata).isAssociation()
+ && PersistentAttributeType.MANY_TO_MANY == getAttribute(property, classMetadata).getPersistentAttributeType();
}
static {
diff --git a/rsql-jpa/pom.xml b/rsql-jpa/pom.xml
index d39c81e0..a0f34862 100644
--- a/rsql-jpa/pom.xml
+++ b/rsql-jpa/pom.xml
@@ -54,8 +54,8 @@
io.hypersistence
- hypersistence-utils-hibernate-60
- 3.5.2
+ hypersistence-utils-hibernate-63
+ 3.9.0
test
diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java
index 37f706e1..20414076 100644
--- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java
+++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java
@@ -20,6 +20,7 @@
import cz.jirutka.rsql.parser.ast.OrNode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
+import org.hibernate.query.criteria.JpaExpression;
@Slf4j
@SuppressWarnings({ "rawtypes", "unchecked" })
@@ -81,7 +82,6 @@ private RSQLJPAContext findPropertyPathInternal(String propertyPath, Path startR
Attribute, ?> attribute = null;
String resolvedPropertyPath = firstTry? mapPropertyPath(propertyPath) : propertyPath;
String[] properties = mapPropertyPath(resolvedPropertyPath).split("\\.");
-
for (int i = 0, propertiesLength = properties.length; i < propertiesLength; i++) {
String property = properties[i];
String mappedProperty = mapProperty(property, classMetadata.getJavaType());
@@ -138,14 +138,14 @@ private RSQLJPAContext findPropertyPathInternal(String propertyPath, Path startR
}
} else if (isElementCollectionType(mappedProperty, classMetadata)) {
String previousClass = classMetadata.getJavaType().getName();
- attribute = classMetadata.getAttribute(property);
+ attribute = RSQLVisitorBase.getAttribute(property, classMetadata);
classMetadata = getManagedElementCollectionType(mappedProperty, classMetadata);
String keyJoin = getKeyJoin(root, mappedProperty);
log.debug("Create a element collection join between [{}] and [{}] using key [{}]", previousClass, classMetadata.getJavaType().getName(), keyJoin);
root = join(keyJoin, root, mappedProperty);
} else if (JsonbSupport.isJsonType(mappedProperty, classMetadata)) {
root = root.get(mappedProperty);
- attribute = classMetadata.getAttribute(mappedProperty);
+ attribute = RSQLVisitorBase.getAttribute(mappedProperty, classMetadata);
break;
} else {
log.debug("Create property path for type [{}] property [{}]", classMetadata.getJavaType().getName(), mappedProperty);
@@ -156,7 +156,7 @@ private RSQLJPAContext findPropertyPathInternal(String propertyPath, Path startR
type = embeddedType;
classMetadata = getManagedType(embeddedType);
} else {
- attribute = classMetadata.getAttribute(property);
+ attribute = RSQLVisitorBase.getAttribute(property, classMetadata);
}
}
}
@@ -244,7 +244,13 @@ private ResolvedExpression resolveExpression(ComparisonNode node, From root, Sel
ComparisonNode jsonbNode = node.withSelector(jsonbPath);
return JsonbSupport.jsonbPathExistsExpression(builder, jsonbNode, path);
} else {
- return ResolvedExpression.ofPath(path.as(String.class), String.class);
+ final Expression expression;
+ if (path instanceof JpaExpression jpaExpression) {
+ expression = jpaExpression.cast(String.class);
+ } else {
+ expression = path.as(String.class);
+ }
+ return ResolvedExpression.ofPath(expression, String.class);
}
} else {
if (attribute != null
diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/SortUtils.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/SortUtils.java
index 8f8ce8c3..46d4f6e3 100644
--- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/SortUtils.java
+++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/SortUtils.java
@@ -13,6 +13,7 @@
import jakarta.persistence.criteria.Order;
import jakarta.persistence.criteria.Root;
+import org.hibernate.query.criteria.JpaExpression;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
@@ -63,16 +64,16 @@ private static Order sortToJpaOrder(final String[] parts, final SortSupport sort
sortSupport.getJoinHints(), sortSupport.getProcedureWhiteList(),
sortSupport.getProcedureBlackList());
+ final boolean ic = parts.length > 2 && "ic".equalsIgnoreCase(parts[2]);
Expression> propertyExpression = selector.getExpression((string, builder) ->{
final RSQLJPAContext rsqljpaContext = converter.findPropertyPath(string, root);
final boolean isJson = JsonbSupport.isJsonType(rsqljpaContext.getAttribute());
return isJson
- ? sortExpressionOfJson(rsqljpaContext, string, sortSupport.getPropertyPathMapper(), builder)
+ ? sortExpressionOfJson(rsqljpaContext, string, sortSupport.getPropertyPathMapper(), builder, ic)
: rsqljpaContext.getPath();
});
- if (parts.length > 2 && "ic".equalsIgnoreCase(parts[2])
- && String.class.isAssignableFrom(propertyExpression.getJavaType())) {
+ if (ic && String.class.isAssignableFrom(propertyExpression.getJavaType())) {
propertyExpression = cb.lower(propertyExpression.as(String.class));
}
@@ -92,7 +93,8 @@ private static Order sortToJpaOrder(final String[] parts, final SortSupport sort
private static Expression> sortExpressionOfJson(RSQLJPAContext context,
String property,
Map mapping,
- CriteriaBuilder builder) {
+ CriteriaBuilder builder,
+ boolean ic) {
String path = PathUtils.expectBestMapping(property, mapping);
String jsonbSelector = JsonbSupport.jsonPathOfSelector(context.getAttribute(), path);
if(jsonbSelector.contains(".")) {
@@ -102,7 +104,11 @@ private static Expression> sortExpressionOfJson(RSQLJPAContext context,
.skip(1) // skip root
.map(builder::literal)
.forEach(args::add);
- return builder.function("jsonb_extract_path", String.class, args.toArray(Expression[]::new));
+ Expression> expression = builder.function("jsonb_extract_path", Object.class, args.toArray(Expression[]::new));
+ if (ic && expression instanceof JpaExpression> jpaExpression) {
+ expression = jpaExpression.cast(String.class);
+ }
+ return expression;
} else {
return context.getPath().as(String.class);
}
diff --git a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java
index b27eddc4..4c0e7be3 100644
--- a/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java
+++ b/rsql-jpa/src/main/java/io/github/perplexhub/rsql/jsonb/JsonbSupport.java
@@ -91,7 +91,7 @@ && isJsonColumn(attribute)
* @return true if the attribute is a jsonb attribute
*/
public static boolean isJsonType(String mappedProperty, ManagedType> classMetadata) {
- return Optional.ofNullable(classMetadata.getAttribute(mappedProperty))
+ return Optional.ofNullable(RSQLVisitorBase.getAttribute(mappedProperty, classMetadata))
.map(JsonbSupport::isJsonType)
.orElse(false);
}
diff --git a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java
index c3bc94cc..4c39ec19 100644
--- a/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java
+++ b/rsql-jpa/src/test/java/io/github/perplexhub/rsql/RSQLJPASupportTest.java
@@ -9,8 +9,10 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
+import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.*;
@@ -764,9 +766,9 @@ final void testBetweenDate() {
@Test
final void testBetweenDateTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
- RSQLJPASupport.addConverter(Date.class, s -> {
+ RSQLJPASupport.addConverter(Timestamp.class, s -> {
try {
- return sdf.parse(s);
+ return new Timestamp(sdf.parse(s).getTime());
} catch (ParseException e) {
return null;
}
diff --git a/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/Application.java b/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/Application.java
index 5e9dba1e..f9f13864 100644
--- a/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/Application.java
+++ b/rsql-querydsl-spring-boot-starter/src/test/java/io/github/perplexhub/rsql/Application.java
@@ -1,5 +1,6 @@
package io.github.perplexhub.rsql;
+import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -23,9 +24,9 @@ public static void main(String[] args) throws Exception {
@Bean
public Object rsqlConfiguration(RSQLCommonSupport rsqlCommonSupport) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
- RSQLCommonSupport.addConverter(Date.class, s -> {
+ RSQLCommonSupport.addConverter(Timestamp.class, s -> {
try {
- return sdf.parse(s);
+ return new Timestamp(sdf.parse(s).getTime());
} catch (Exception e) {
return null;
}