Skip to content

Commit 3d89f9e

Browse files
committed
HHH-8802 - Class cast exception thrown when trying to access Embedded type relationship path in Tuple
(cherry picked from commit 2e6811f)
1 parent 282a7b1 commit 3d89f9e

File tree

5 files changed

+266
-7
lines changed

5 files changed

+266
-7
lines changed

hibernate-entitymanager/src/main/java/org/hibernate/jpa/criteria/path/SingularAttributeJoin.java

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@
99
import javax.persistence.criteria.JoinType;
1010
import javax.persistence.metamodel.Attribute;
1111
import javax.persistence.metamodel.Bindable;
12+
import javax.persistence.metamodel.ManagedType;
13+
import javax.persistence.metamodel.PluralAttribute;
1214
import javax.persistence.metamodel.SingularAttribute;
15+
import javax.persistence.metamodel.Type;
1316

1417
import org.hibernate.jpa.criteria.CriteriaBuilderImpl;
1518
import org.hibernate.jpa.criteria.CriteriaSubqueryImpl;
@@ -36,13 +39,17 @@ public SingularAttributeJoin(
3639
SingularAttribute<? super O, ?> joinAttribute,
3740
JoinType joinType) {
3841
super( criteriaBuilder, javaType, pathSource, joinAttribute, joinType );
39-
this.model = (Bindable<X>) (
40-
Attribute.PersistentAttributeType.EMBEDDED == joinAttribute.getPersistentAttributeType()
41-
? joinAttribute
42-
: javaType != null
43-
? criteriaBuilder.getEntityManagerFactory().getMetamodel().managedType( javaType )
44-
: joinAttribute.getType()
45-
);
42+
if ( Attribute.PersistentAttributeType.EMBEDDED == joinAttribute.getPersistentAttributeType() ) {
43+
this.model = (Bindable<X>) joinAttribute;
44+
}
45+
else {
46+
if ( javaType != null ) {
47+
this.model = (Bindable<X>) criteriaBuilder.getEntityManagerFactory().getMetamodel().managedType( javaType );
48+
}
49+
else {
50+
this.model = (Bindable<X>) joinAttribute.getType();
51+
}
52+
}
4653
}
4754

4855
@Override
@@ -71,6 +78,34 @@ protected boolean canBeJoinSource() {
7178
return true;
7279
}
7380

81+
@Override
82+
@SuppressWarnings("unchecked")
83+
protected ManagedType<? super X> locateManagedType() {
84+
if ( getModel().getBindableType() == Bindable.BindableType.ENTITY_TYPE ) {
85+
return (ManagedType<? super X>) getModel();
86+
}
87+
else if ( getModel().getBindableType() == Bindable.BindableType.SINGULAR_ATTRIBUTE ) {
88+
final Type joinedAttributeType = ( (SingularAttribute) getAttribute() ).getType();
89+
if ( !ManagedType.class.isInstance( joinedAttributeType ) ) {
90+
throw new UnsupportedOperationException(
91+
"Cannot further dereference attribute join [" + getPathIdentifier() + "] as its type is not a ManagedType"
92+
);
93+
}
94+
return (ManagedType<? super X>) joinedAttributeType;
95+
}
96+
else if ( getModel().getBindableType() == Bindable.BindableType.PLURAL_ATTRIBUTE ) {
97+
final Type elementType = ( (PluralAttribute) getAttribute() ).getElementType();
98+
if ( !ManagedType.class.isInstance( elementType ) ) {
99+
throw new UnsupportedOperationException(
100+
"Cannot further dereference attribute join [" + getPathIdentifier() + "] (plural) as its element type is not a ManagedType"
101+
);
102+
}
103+
return (ManagedType<? super X>) elementType;
104+
}
105+
106+
return super.locateManagedType();
107+
}
108+
74109
public Bindable<X> getModel() {
75110
return model;
76111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.jpa.test.criteria.components.joins;
8+
9+
import java.util.List;
10+
import javax.persistence.EntityManager;
11+
import javax.persistence.Tuple;
12+
import javax.persistence.criteria.CriteriaBuilder;
13+
import javax.persistence.criteria.CriteriaQuery;
14+
import javax.persistence.criteria.Join;
15+
import javax.persistence.criteria.JoinType;
16+
import javax.persistence.criteria.Path;
17+
import javax.persistence.criteria.Root;
18+
import javax.persistence.metamodel.SingularAttribute;
19+
20+
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
21+
22+
import org.junit.After;
23+
import org.junit.Before;
24+
import org.junit.Test;
25+
26+
import static org.junit.Assert.assertEquals;
27+
28+
public class ComponentJoinTest extends BaseEntityManagerFunctionalTestCase {
29+
@Override
30+
protected Class<?>[] getAnnotatedClasses() {
31+
return new Class[] { Entity.class, EmbeddedType.class, ManyToOneType.class };
32+
}
33+
34+
public static final String THEVALUE = "thevalue";
35+
36+
@Before
37+
public void before() {
38+
EntityManager entityManager = entityManagerFactory().createEntityManager();
39+
entityManager.getTransaction().begin();
40+
ManyToOneType manyToOneType = new ManyToOneType( THEVALUE );
41+
EmbeddedType embeddedType = new EmbeddedType( manyToOneType );
42+
Entity entity = new Entity( embeddedType );
43+
entityManager.persist( entity );
44+
entityManager.getTransaction().commit();
45+
entityManager.close();
46+
}
47+
48+
@After
49+
public void after() {
50+
EntityManager entityManager = entityManagerFactory().createEntityManager();
51+
entityManager.getTransaction().begin();
52+
entityManager.createQuery( "delete Entity" ).executeUpdate();
53+
entityManager.createQuery( "delete ManyToOneType" ).executeUpdate();
54+
entityManager.getTransaction().commit();
55+
entityManager.close();
56+
}
57+
58+
interface JoinBuilder {
59+
Join<EmbeddedType,ManyToOneType> buildJoinToManyToOneType(Join<Entity, EmbeddedType> source);
60+
}
61+
62+
private void doTest(JoinBuilder joinBuilder) {
63+
EntityManager entityManager = getOrCreateEntityManager();
64+
entityManager.getTransaction().begin();
65+
66+
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
67+
CriteriaQuery<Tuple> criteriaQuery = builder.createTupleQuery();
68+
Root<Entity> root = criteriaQuery.from( Entity.class );
69+
Join<Entity, EmbeddedType> join = root.join( "embeddedType", JoinType.LEFT );
70+
71+
// left join to the manyToOne on the embeddable with a string property
72+
Path<String> path = joinBuilder.buildJoinToManyToOneType( join ).get( "value" );
73+
74+
// select the path in the tuple
75+
criteriaQuery.select( builder.tuple( path ) );
76+
77+
List<Tuple> results = entityManager.createQuery( criteriaQuery ).getResultList();
78+
Tuple result = results.iterator().next();
79+
80+
assertEquals( THEVALUE, result.get(0) );
81+
82+
entityManager.getTransaction().commit();
83+
entityManager.close();
84+
}
85+
86+
@Test
87+
public void getResultWithStringPropertyDerivedPath() {
88+
doTest(
89+
new JoinBuilder() {
90+
@Override
91+
public Join<EmbeddedType, ManyToOneType> buildJoinToManyToOneType(Join<Entity, EmbeddedType> source) {
92+
return source.join( "manyToOneType", JoinType.LEFT );
93+
}
94+
}
95+
);
96+
}
97+
98+
@Test
99+
@SuppressWarnings("unchecked")
100+
public void getResultWithMetamodelDerivedPath() {
101+
doTest(
102+
new JoinBuilder() {
103+
@Override
104+
public Join<EmbeddedType, ManyToOneType> buildJoinToManyToOneType(Join<Entity, EmbeddedType> source) {
105+
final SingularAttribute<EmbeddedType, ManyToOneType> attr =
106+
(SingularAttribute<EmbeddedType, ManyToOneType>) entityManagerFactory().getMetamodel()
107+
.managedType( EmbeddedType.class )
108+
.getDeclaredSingularAttribute( "manyToOneType" );
109+
return source.join( attr, JoinType.LEFT );
110+
}
111+
}
112+
);
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
8+
package org.hibernate.jpa.test.criteria.components.joins;
9+
10+
import javax.persistence.CascadeType;
11+
import javax.persistence.Embeddable;
12+
import javax.persistence.ManyToOne;
13+
14+
/**
15+
* @author Matt Todd
16+
*/
17+
@Embeddable
18+
public class EmbeddedType {
19+
@ManyToOne(cascade = CascadeType.ALL)
20+
private ManyToOneType manyToOneType;
21+
22+
public EmbeddedType() {
23+
}
24+
25+
public EmbeddedType(ManyToOneType manyToOneType) {
26+
this.manyToOneType = manyToOneType;
27+
}
28+
29+
public ManyToOneType getManyToOneType() {
30+
return manyToOneType;
31+
}
32+
33+
public void setManyToOneType(ManyToOneType manyToOneType) {
34+
this.manyToOneType = manyToOneType;
35+
}
36+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.jpa.test.criteria.components.joins;
8+
9+
import javax.persistence.Embedded;
10+
import javax.persistence.GeneratedValue;
11+
import javax.persistence.Id;
12+
13+
@javax.persistence.Entity
14+
public class Entity {
15+
@Id
16+
@GeneratedValue
17+
private Long id;
18+
19+
@Embedded
20+
private EmbeddedType embeddedType;
21+
22+
public Entity() {
23+
// for jpa
24+
}
25+
26+
public Entity(EmbeddedType embeddedType) {
27+
this.embeddedType = embeddedType;
28+
}
29+
30+
public EmbeddedType getEmbeddedType() {
31+
return embeddedType;
32+
}
33+
34+
public void setEmbeddedType(EmbeddedType embeddedType) {
35+
this.embeddedType = embeddedType;
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.jpa.test.criteria.components.joins;
8+
9+
import javax.persistence.Entity;
10+
import javax.persistence.GeneratedValue;
11+
import javax.persistence.Id;
12+
13+
/**
14+
* @author Matt Todd
15+
*/
16+
@Entity
17+
public class ManyToOneType {
18+
@Id
19+
@GeneratedValue
20+
public Long id;
21+
private String value;
22+
23+
public ManyToOneType() {
24+
}
25+
26+
public ManyToOneType(String value) {
27+
this.value = value;
28+
}
29+
30+
public String getValue() {
31+
return value;
32+
}
33+
34+
public void setValue(String value) {
35+
this.value = value;
36+
}
37+
}

0 commit comments

Comments
 (0)