Throw IllegalArgumentException when the an attribute name is not resolved as a subPath
This commit is contained in:
parent
326f2ae775
commit
22457cc74d
|
@ -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)}.
|
||||
*/
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue