HHH-8802 - Class cast exception thrown when trying to access Embedded type relationship path in Tuple

(cherry picked from commit 2e6811f413)
This commit is contained in:
Steve Ebersole 2015-11-05 09:35:14 -06:00
parent 282a7b1afc
commit 3d89f9e771
5 changed files with 266 additions and 7 deletions

View File

@ -9,7 +9,10 @@ package org.hibernate.jpa.criteria.path;
import javax.persistence.criteria.JoinType;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Bindable;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.PluralAttribute;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.Type;
import org.hibernate.jpa.criteria.CriteriaBuilderImpl;
import org.hibernate.jpa.criteria.CriteriaSubqueryImpl;
@ -36,13 +39,17 @@ public class SingularAttributeJoin<O,X> extends AbstractJoinImpl<O,X> {
SingularAttribute<? super O, ?> joinAttribute,
JoinType joinType) {
super( criteriaBuilder, javaType, pathSource, joinAttribute, joinType );
this.model = (Bindable<X>) (
Attribute.PersistentAttributeType.EMBEDDED == joinAttribute.getPersistentAttributeType()
? joinAttribute
: javaType != null
? criteriaBuilder.getEntityManagerFactory().getMetamodel().managedType( javaType )
: joinAttribute.getType()
);
if ( Attribute.PersistentAttributeType.EMBEDDED == joinAttribute.getPersistentAttributeType() ) {
this.model = (Bindable<X>) joinAttribute;
}
else {
if ( javaType != null ) {
this.model = (Bindable<X>) criteriaBuilder.getEntityManagerFactory().getMetamodel().managedType( javaType );
}
else {
this.model = (Bindable<X>) joinAttribute.getType();
}
}
}
@Override
@ -71,6 +78,34 @@ public class SingularAttributeJoin<O,X> extends AbstractJoinImpl<O,X> {
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() {
return model;
}

View File

@ -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 );
}
}
);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}