HHH-8802 - Class cast exception thrown when trying to access Embedded type relationship path in Tuple
This commit is contained in:
parent
ec8794bbd0
commit
2e6811f413
|
@ -9,7 +9,10 @@ package org.hibernate.jpa.criteria.path;
|
||||||
import javax.persistence.criteria.JoinType;
|
import javax.persistence.criteria.JoinType;
|
||||||
import javax.persistence.metamodel.Attribute;
|
import javax.persistence.metamodel.Attribute;
|
||||||
import javax.persistence.metamodel.Bindable;
|
import javax.persistence.metamodel.Bindable;
|
||||||
|
import javax.persistence.metamodel.ManagedType;
|
||||||
|
import javax.persistence.metamodel.PluralAttribute;
|
||||||
import javax.persistence.metamodel.SingularAttribute;
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
import javax.persistence.metamodel.Type;
|
||||||
|
|
||||||
import org.hibernate.jpa.criteria.CriteriaBuilderImpl;
|
import org.hibernate.jpa.criteria.CriteriaBuilderImpl;
|
||||||
import org.hibernate.jpa.criteria.CriteriaSubqueryImpl;
|
import org.hibernate.jpa.criteria.CriteriaSubqueryImpl;
|
||||||
|
@ -36,13 +39,17 @@ public class SingularAttributeJoin<O,X> extends AbstractJoinImpl<O,X> {
|
||||||
SingularAttribute<? super O, ?> joinAttribute,
|
SingularAttribute<? super O, ?> joinAttribute,
|
||||||
JoinType joinType) {
|
JoinType joinType) {
|
||||||
super( criteriaBuilder, javaType, pathSource, joinAttribute, joinType );
|
super( criteriaBuilder, javaType, pathSource, joinAttribute, joinType );
|
||||||
this.model = (Bindable<X>) (
|
if ( Attribute.PersistentAttributeType.EMBEDDED == joinAttribute.getPersistentAttributeType() ) {
|
||||||
Attribute.PersistentAttributeType.EMBEDDED == joinAttribute.getPersistentAttributeType()
|
this.model = (Bindable<X>) joinAttribute;
|
||||||
? joinAttribute
|
}
|
||||||
: javaType != null
|
else {
|
||||||
? criteriaBuilder.getEntityManagerFactory().getMetamodel().managedType( javaType )
|
if ( javaType != null ) {
|
||||||
: joinAttribute.getType()
|
this.model = (Bindable<X>) criteriaBuilder.getEntityManagerFactory().getMetamodel().managedType( javaType );
|
||||||
);
|
}
|
||||||
|
else {
|
||||||
|
this.model = (Bindable<X>) joinAttribute.getType();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -71,6 +78,34 @@ public class SingularAttributeJoin<O,X> extends AbstractJoinImpl<O,X> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected ManagedType<? super X> locateManagedType() {
|
||||||
|
if ( getModel().getBindableType() == Bindable.BindableType.ENTITY_TYPE ) {
|
||||||
|
return (ManagedType<? super X>) getModel();
|
||||||
|
}
|
||||||
|
else if ( getModel().getBindableType() == Bindable.BindableType.SINGULAR_ATTRIBUTE ) {
|
||||||
|
final Type joinedAttributeType = ( (SingularAttribute) getAttribute() ).getType();
|
||||||
|
if ( !ManagedType.class.isInstance( joinedAttributeType ) ) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Cannot further dereference attribute join [" + getPathIdentifier() + "] as its type is not a ManagedType"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (ManagedType<? super X>) joinedAttributeType;
|
||||||
|
}
|
||||||
|
else if ( getModel().getBindableType() == Bindable.BindableType.PLURAL_ATTRIBUTE ) {
|
||||||
|
final Type elementType = ( (PluralAttribute) getAttribute() ).getElementType();
|
||||||
|
if ( !ManagedType.class.isInstance( elementType ) ) {
|
||||||
|
throw new UnsupportedOperationException(
|
||||||
|
"Cannot further dereference attribute join [" + getPathIdentifier() + "] (plural) as its element type is not a ManagedType"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (ManagedType<? super X>) elementType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.locateManagedType();
|
||||||
|
}
|
||||||
|
|
||||||
public Bindable<X> getModel() {
|
public Bindable<X> getModel() {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.criteria.components.joins;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Tuple;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Join;
|
||||||
|
import javax.persistence.criteria.JoinType;
|
||||||
|
import javax.persistence.criteria.Path;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.persistence.metamodel.SingularAttribute;
|
||||||
|
|
||||||
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class ComponentJoinTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class[] { Entity.class, EmbeddedType.class, ManyToOneType.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final String THEVALUE = "thevalue";
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
EntityManager entityManager = entityManagerFactory().createEntityManager();
|
||||||
|
entityManager.getTransaction().begin();
|
||||||
|
ManyToOneType manyToOneType = new ManyToOneType( THEVALUE );
|
||||||
|
EmbeddedType embeddedType = new EmbeddedType( manyToOneType );
|
||||||
|
Entity entity = new Entity( embeddedType );
|
||||||
|
entityManager.persist( entity );
|
||||||
|
entityManager.getTransaction().commit();
|
||||||
|
entityManager.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void after() {
|
||||||
|
EntityManager entityManager = entityManagerFactory().createEntityManager();
|
||||||
|
entityManager.getTransaction().begin();
|
||||||
|
entityManager.createQuery( "delete Entity" ).executeUpdate();
|
||||||
|
entityManager.createQuery( "delete ManyToOneType" ).executeUpdate();
|
||||||
|
entityManager.getTransaction().commit();
|
||||||
|
entityManager.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
interface JoinBuilder {
|
||||||
|
Join<EmbeddedType,ManyToOneType> buildJoinToManyToOneType(Join<Entity, EmbeddedType> source);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doTest(JoinBuilder joinBuilder) {
|
||||||
|
EntityManager entityManager = getOrCreateEntityManager();
|
||||||
|
entityManager.getTransaction().begin();
|
||||||
|
|
||||||
|
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<Tuple> criteriaQuery = builder.createTupleQuery();
|
||||||
|
Root<Entity> root = criteriaQuery.from( Entity.class );
|
||||||
|
Join<Entity, EmbeddedType> join = root.join( "embeddedType", JoinType.LEFT );
|
||||||
|
|
||||||
|
// left join to the manyToOne on the embeddable with a string property
|
||||||
|
Path<String> path = joinBuilder.buildJoinToManyToOneType( join ).get( "value" );
|
||||||
|
|
||||||
|
// select the path in the tuple
|
||||||
|
criteriaQuery.select( builder.tuple( path ) );
|
||||||
|
|
||||||
|
List<Tuple> results = entityManager.createQuery( criteriaQuery ).getResultList();
|
||||||
|
Tuple result = results.iterator().next();
|
||||||
|
|
||||||
|
assertEquals( THEVALUE, result.get(0) );
|
||||||
|
|
||||||
|
entityManager.getTransaction().commit();
|
||||||
|
entityManager.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getResultWithStringPropertyDerivedPath() {
|
||||||
|
doTest(
|
||||||
|
new JoinBuilder() {
|
||||||
|
@Override
|
||||||
|
public Join<EmbeddedType, ManyToOneType> buildJoinToManyToOneType(Join<Entity, EmbeddedType> source) {
|
||||||
|
return source.join( "manyToOneType", JoinType.LEFT );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void getResultWithMetamodelDerivedPath() {
|
||||||
|
doTest(
|
||||||
|
new JoinBuilder() {
|
||||||
|
@Override
|
||||||
|
public Join<EmbeddedType, ManyToOneType> buildJoinToManyToOneType(Join<Entity, EmbeddedType> source) {
|
||||||
|
final SingularAttribute<EmbeddedType, ManyToOneType> attr =
|
||||||
|
(SingularAttribute<EmbeddedType, ManyToOneType>) entityManagerFactory().getMetamodel()
|
||||||
|
.managedType( EmbeddedType.class )
|
||||||
|
.getDeclaredSingularAttribute( "manyToOneType" );
|
||||||
|
return source.join( attr, JoinType.LEFT );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.criteria.components.joins;
|
||||||
|
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Embeddable;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Matt Todd
|
||||||
|
*/
|
||||||
|
@Embeddable
|
||||||
|
public class EmbeddedType {
|
||||||
|
@ManyToOne(cascade = CascadeType.ALL)
|
||||||
|
private ManyToOneType manyToOneType;
|
||||||
|
|
||||||
|
public EmbeddedType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmbeddedType(ManyToOneType manyToOneType) {
|
||||||
|
this.manyToOneType = manyToOneType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManyToOneType getManyToOneType() {
|
||||||
|
return manyToOneType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setManyToOneType(ManyToOneType manyToOneType) {
|
||||||
|
this.manyToOneType = manyToOneType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.criteria.components.joins;
|
||||||
|
|
||||||
|
import javax.persistence.Embedded;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
@javax.persistence.Entity
|
||||||
|
public class Entity {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Embedded
|
||||||
|
private EmbeddedType embeddedType;
|
||||||
|
|
||||||
|
public Entity() {
|
||||||
|
// for jpa
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity(EmbeddedType embeddedType) {
|
||||||
|
this.embeddedType = embeddedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmbeddedType getEmbeddedType() {
|
||||||
|
return embeddedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmbeddedType(EmbeddedType embeddedType) {
|
||||||
|
this.embeddedType = embeddedType;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* 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.jpa.test.criteria.components.joins;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Matt Todd
|
||||||
|
*/
|
||||||
|
@Entity
|
||||||
|
public class ManyToOneType {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue
|
||||||
|
public Long id;
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public ManyToOneType() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public ManyToOneType(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue