Throw IllegalArgumentException when the an attribute name is not resolved as a subPath

This commit is contained in:
Andrea Boriero 2022-01-14 09:15:12 +01:00 committed by Steve Ebersole
parent 326f2ae775
commit 22457cc74d
4 changed files with 165 additions and 21 deletions

View File

@ -9,6 +9,7 @@ package org.hibernate.query.sqm;
import jakarta.persistence.metamodel.Bindable;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.tree.SqmExpressableAccessor;
import org.hibernate.query.sqm.tree.domain.SqmPath;
@ -37,10 +38,34 @@ public interface SqmPathSource<J> extends SqmExpressable<J>, Bindable<J>, SqmExp
/**
* Find a SqmPathSource by name relative to this source.
*
* returns null if the subPathSource is not found
*
* @throws IllegalStateException to indicate that this source cannot be de-referenced
*/
SqmPathSource<?> findSubPathSource(String name);
/**
* Find a SqmPathSource by name relative to this source.
*
* @throws IllegalStateException to indicate that this source cannot be de-referenced
* @throws IllegalArgumentException if the subPathSource is not found
*/
default SqmPathSource<?> getSubPathSource(String name) {
final SqmPathSource<?> subPathSource = findSubPathSource( name );
if ( subPathSource == null ) {
throw new IllegalArgumentException(
new SemanticException(
String.format(
"Could not resolve attribute '%s' of '%s'",
name,
getExpressable().getExpressableJavaTypeDescriptor().getJavaType().getTypeName()
)
)
);
}
return subPathSource;
}
/**
* Returns the intermediate SqmPathSource for a path source previously acquired via {@link #findSubPathSource(String)}.
*/

View File

@ -384,7 +384,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override
@SuppressWarnings("unchecked")
public <X, Y> SqmAttributeJoin<X, Y> join(String attributeName, JoinType jt) {
final SqmPathSource<?> subPathSource = getReferencedPathSource().findSubPathSource( attributeName );
final SqmPathSource<?> subPathSource = getReferencedPathSource().getSubPathSource( attributeName );
return (SqmAttributeJoin<X, Y>) buildJoin( subPathSource, SqmJoinType.from( jt ), false );
}
@ -396,7 +396,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override
@SuppressWarnings("unchecked")
public <X, Y> SqmBagJoin<X, Y> joinCollection(String attributeName, JoinType jt) {
final SqmPathSource<?> joinedPathSource = getReferencedPathSource().findSubPathSource( attributeName );
final SqmPathSource<?> joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName );
if ( joinedPathSource instanceof BagPersistentAttribute ) {
final SqmBagJoin<T, Y> join = buildBagJoin(
@ -427,7 +427,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override
@SuppressWarnings("unchecked")
public <X, Y> SqmSetJoin<X, Y> joinSet(String attributeName, JoinType jt) {
final SqmPathSource<?> joinedPathSource = getReferencedPathSource().findSubPathSource( attributeName );
final SqmPathSource<?> joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName );
if ( joinedPathSource instanceof SetPersistentAttribute ) {
final SqmSetJoin<T, Y> join = buildSetJoin(
@ -458,7 +458,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override
@SuppressWarnings("unchecked")
public <X, Y> SqmListJoin<X, Y> joinList(String attributeName, JoinType jt) {
final SqmPathSource<?> joinedPathSource = getReferencedPathSource().findSubPathSource( attributeName );
final SqmPathSource<?> joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName );
if ( joinedPathSource instanceof ListPersistentAttribute ) {
final SqmListJoin<T, Y> join = buildListJoin(
@ -489,7 +489,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
@Override
@SuppressWarnings("unchecked")
public <X, K, V> SqmMapJoin<X, K, V> joinMap(String attributeName, JoinType jt) {
final SqmPathSource<?> joinedPathSource = getReferencedPathSource().findSubPathSource( attributeName );
final SqmPathSource<?> joinedPathSource = getReferencedPathSource().getSubPathSource( attributeName );
if ( joinedPathSource instanceof MapPersistentAttribute<?, ?, ?> ) {
final SqmMapJoin<T, K, V> join = buildMapJoin(

View File

@ -140,18 +140,8 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
// 1) add `Navigable#createCriteriaExpression` (ala, the exist `#createSqmExpression`)
// 2) remove `Navigable#createSqmExpression` and use the approach used here instead.
final SqmPathSource<?> subNavigable = getReferencedPathSource().findSubPathSource( attributeName );
final SqmPathSource<?> subNavigable = getReferencedPathSource().getSubPathSource( attributeName );
if ( subNavigable == null ) {
throw new IllegalArgumentException(
new SemanticException(
String.format(
"Could not resolve attribute '%s' of '%s'",
attributeName, getNavigablePath()
)
)
);
}
return resolvePath( attributeName, subNavigable );
}

View File

@ -15,15 +15,22 @@ import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.From;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.criteria.Selection;
import static org.junit.jupiter.api.Assertions.fail;
@Jpa(
annotatedClasses = IllegalArgumentExceptionTest.Person.class
annotatedClasses = {
IllegalArgumentExceptionTest.Person.class,
IllegalArgumentExceptionTest.Address.class
}
)
public class IllegalArgumentExceptionTest {
@ -74,8 +81,8 @@ public class IllegalArgumentExceptionTest {
try {
query.multiselect(
person.get( "not_existing" ).alias( "a1" ),
person.get( "another_not_existing" ).alias( "a2" )
person.get( "not_existing_attribute_name" ).alias( "a1" ),
person.get( "another_not_existing_attribute_name" ).alias( "a2" )
);
fail( "TCK expects an IllegalArgumentException" );
}
@ -87,10 +94,12 @@ public class IllegalArgumentExceptionTest {
@Test
public void testCriteriaStringQuery(EntityManagerFactoryScope scope) {
final CriteriaQuery<String> query = scope.getEntityManagerFactory().getCriteriaBuilder().createQuery( String.class );
final CriteriaQuery<String> query = scope.getEntityManagerFactory()
.getCriteriaBuilder()
.createQuery( String.class );
try {
final Root<Person> person = query.from( Person.class );
person.get( "not_existing" );
person.get( "not_existing_attribute_name" );
fail( "TCK expects an IllegalArgumentException" );
}
@ -99,6 +108,84 @@ public class IllegalArgumentExceptionTest {
}
}
@Test
public void testGetStringNonExistingAttributeName(EntityManagerFactoryScope scope) {
try {
final CriteriaQuery<Person> query = scope.getEntityManagerFactory()
.getCriteriaBuilder()
.createQuery( Person.class );
query.from( Person.class ).get( "not_existing_attribute_name" );
fail( "TCK expects an IllegalArgumentException" );
}
catch (IllegalArgumentException iae) {
//expected by TCK
}
}
@Test
public void testJoinANonExistingAttributeNameToAFrom(EntityManagerFactoryScope scope) {
final CriteriaQuery<Person> query = scope.getEntityManagerFactory()
.getCriteriaBuilder()
.createQuery( Person.class );
final From<Person, Person> customer = query.from( Person.class );
try {
customer.join( "not_existing_attribute_name" );
fail( "TCK expects an IllegalArgumentException" );
}
catch (IllegalArgumentException iae) {
//expected by TCK
}
}
@Test
public void testJoinANonExistingAttributeNameToAFrom2(EntityManagerFactoryScope scope) {
final CriteriaQuery<Person> query = scope.getEntityManagerFactory()
.getCriteriaBuilder()
.createQuery( Person.class );
final From<Person, Person> customer = query.from( Person.class );
try {
customer.join( "not_existing_attribute_name", JoinType.INNER );
fail( "TCK expects an IllegalArgumentException" );
}
catch (IllegalArgumentException iae) {
//expected by TCK
}
}
@Test
public void testJoinANonExistingAttributeNameToAJoin(EntityManagerFactoryScope scope) {
final CriteriaQuery<Person> query = scope.getEntityManagerFactory()
.getCriteriaBuilder()
.createQuery( Person.class );
Root<Person> customer = query.from( Person.class );
Join<Person, Address> address = customer.join( "address" );
try {
address.join( "not_existing_attribute_name" );
fail( "TCK expects an IllegalArgumentException" );
}
catch (IllegalArgumentException iae) {
//expected by TCK
}
}
@Test
public void testJoinANonExistingAttributeNameToAJoin2(EntityManagerFactoryScope scope) {
final CriteriaQuery<Person> query = scope.getEntityManagerFactory()
.getCriteriaBuilder()
.createQuery( Person.class );
Root<Person> customer = query.from( Person.class );
Join<Person, Address> address = customer.join( "address" );
try {
address.join( "not_existing_attribute_name", JoinType.INNER );
fail( "TCK expects an IllegalArgumentException" );
}
catch (IllegalArgumentException iae) {
//expected by TCK
}
}
@Entity(name = "Person")
public static class Person {
@ -107,6 +194,9 @@ public class IllegalArgumentExceptionTest {
private String name;
@ManyToOne
private Address address;
Person() {
}
@ -115,4 +205,43 @@ public class IllegalArgumentExceptionTest {
this.name = name;
}
}
@Entity(name = "Address")
public static class Address {
@Id
private Integer id;
private String street;
private String city;
private String zipcode;
Address() {
}
public Address(Integer id, String street, String city, String zipcode) {
this.id = id;
this.street = street;
this.city = city;
this.zipcode = zipcode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
}
}