[HHH-17416] Add new inheritor JavaObjectType for specifying unresolved query parameter

This commit is contained in:
The-Huginn 2023-11-22 11:46:26 +01:00 committed by Steve Ebersole
parent 71c95f1699
commit 67cdd0b28a
4 changed files with 118 additions and 4 deletions

View File

@ -27,6 +27,7 @@
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.type.BasicPluralType;
import org.hibernate.type.BasicType;
import org.hibernate.type.QueryParameterJavaObjectType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import java.time.temporal.Temporal;
@ -107,6 +108,12 @@ public static boolean areTypesComparable(
return true;
}
// for query with parameters we are unable to resolve the correct JavaType, especially for tuple of parameters
if ( lhsType instanceof QueryParameterJavaObjectType || rhsType instanceof QueryParameterJavaObjectType) {
return true;
}
// since we can't so anything meaningful here, just allow
// any comparison with multivalued parameters

View File

@ -0,0 +1,21 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.type;
public class QueryParameterJavaObjectType extends JavaObjectType {
public static final QueryParameterJavaObjectType INSTANCE = new QueryParameterJavaObjectType();
public QueryParameterJavaObjectType() {
super();
}
@Override
public String getName() {
return "QUERY_PARAMETER_JAVA_OBJECT";
}
}

View File

@ -57,12 +57,14 @@
import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.QueryParameterJavaObjectType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.JavaType;
@ -591,10 +593,17 @@ private Class<?> entityClassForEntityName(String entityName) {
public SqmExpressible<?> resolveTupleType(List<? extends SqmTypedNode<?>> typedNodes) {
final SqmExpressible<?>[] components = new SqmExpressible<?>[typedNodes.size()];
for ( int i = 0; i < typedNodes.size(); i++ ) {
final SqmExpressible<?> sqmExpressible = typedNodes.get( i ).getNodeType();
components[i] = sqmExpressible != null
? sqmExpressible
: getBasicTypeForJavaType( Object.class );
SqmTypedNode<?> tupleElement = typedNodes.get(i);
final SqmExpressible<?> sqmExpressible = tupleElement.getNodeType();
// keep null value for Named Parameters
if (tupleElement instanceof SqmParameter<?> && sqmExpressible == null) {
components[i] = QueryParameterJavaObjectType.INSTANCE;
}
else {
components[i] = sqmExpressible != null
? sqmExpressible
: getBasicTypeForJavaType( Object.class );
}
}
return arrayTuples.computeIfAbsent(
new ArrayCacheKey( components ),

View File

@ -0,0 +1,77 @@
package org.hibernate.orm.test.query.hql;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import org.hibernate.orm.test.query.sqm.domain.Person;
import org.hibernate.orm.test.query.sqm.domain.Person_;
import org.hibernate.query.SelectionQuery;
import org.hibernate.query.SemanticException;
import org.hibernate.testing.orm.junit.BaseSessionFactoryFunctionalTest;
import org.hibernate.testing.orm.junit.JiraKey;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@JiraKey(value = "HHH-17416")
public class HHH17416Test extends BaseSessionFactoryFunctionalTest {
private static final Person person = new Person();
static {
person.setPk(7);
person.setNickName("Tadpole");
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {Person.class};
}
@BeforeEach
public void setup() {
inTransaction(session -> session.persist(person));
}
@AfterEach
public void teardown() {
inTransaction(session -> session.createMutationQuery("delete from Person").executeUpdate());
}
@Test
public void testWhereClauseWithTuple() {
sessionFactoryScope().inTransaction(
entityManager -> {
SelectionQuery<Person> selectionQuery = entityManager.createSelectionQuery("from Person p where (p.id, p.nickName) = (:val1, :val2)", Person.class);
selectionQuery = selectionQuery.setParameter("val1", person.getPk()).setParameter("val2", person.getNickName());
Person retrievedPerson = selectionQuery.getSingleResult();
Assertions.assertEquals(person.getPk(), retrievedPerson.getPk());
Assertions.assertEquals(person.getNickName(), retrievedPerson.getNickName());
}
);
}
@Test
public void testWhereClauseWithInvalidObjectType() {
sessionFactoryScope().inTransaction(
entityManager -> {
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> criteria = builder.createQuery(Person.class);
Root<Person> root = criteria.from(Person.class);
criteria.select(root);
try {
criteria.where(builder.equal(root.get(Person_.nickName), builder.literal(new Object())));
TypedQuery<Person> ignored = entityManager.createQuery(criteria);
Assertions.fail("Should have failed with 'Cannot compare left expression of type' of type `org.hibernate.query.SemanticException'");
}
catch (Exception e) {
Assertions.assertTrue(e instanceof SemanticException);
Assertions.assertTrue(e.getMessage().startsWith("Cannot compare left expression of type"));
}
}
);
}
}