Skip to content

Commit 8f4450c

Browse files
beikovSanne
authored andcommitted
HHH-14597 Test and fix for NPE while trying to delete cascade to-one association within element collection
1 parent 7329f44 commit 8f4450c

File tree

7 files changed

+261
-13
lines changed

7 files changed

+261
-13
lines changed

hibernate-core/src/main/java/org/hibernate/engine/internal/Cascade.java

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -232,10 +232,12 @@ private static void cascadeProperty(
232232
}
233233
}
234234
else if ( type.isComponentType() ) {
235-
if ( componentPath == null ) {
235+
if ( componentPath == null && propertyName != null ) {
236236
componentPath = new ArrayList<>();
237237
}
238-
componentPath.add( propertyName );
238+
if ( componentPath != null ) {
239+
componentPath.add( propertyName );
240+
}
239241
cascadeComponent(
240242
action,
241243
cascadePoint,
@@ -246,7 +248,9 @@ else if ( type.isComponentType() ) {
246248
(CompositeType) type,
247249
anything
248250
);
249-
componentPath.remove( componentPath.size() - 1 );
251+
if ( componentPath != null ) {
252+
componentPath.remove( componentPath.size() - 1 );
253+
}
250254
}
251255
}
252256

@@ -293,17 +297,24 @@ private static void cascadeLogicalOneToOneOrphanRemoval(
293297
// Since the loadedState in the EntityEntry is a flat domain type array
294298
// We first have to extract the component object and then ask the component type
295299
// recursively to give us the value of the sub-property of that object
296-
loadedValue = entry.getLoadedValue( componentPath.get( 0 ) );
297-
ComponentType componentType = (ComponentType) entry.getPersister().getPropertyType( componentPath.get( 0 ) );
298-
if ( componentPath.size() != 1 ) {
299-
for ( int i = 1; i < componentPath.size(); i++ ) {
300-
final int subPropertyIndex = componentType.getPropertyIndex( componentPath.get( i ) );
301-
loadedValue = componentType.getPropertyValue( loadedValue, subPropertyIndex );
302-
componentType = (ComponentType) componentType.getSubtypes()[subPropertyIndex];
300+
final Type propertyType = entry.getPersister().getPropertyType( componentPath.get(0) );
301+
if ( propertyType instanceof ComponentType ) {
302+
loadedValue = entry.getLoadedValue( componentPath.get( 0 ) );
303+
ComponentType componentType = (ComponentType) propertyType;
304+
if ( componentPath.size() != 1 ) {
305+
for ( int i = 1; i < componentPath.size(); i++ ) {
306+
final int subPropertyIndex = componentType.getPropertyIndex( componentPath.get( i ) );
307+
loadedValue = componentType.getPropertyValue( loadedValue, subPropertyIndex );
308+
componentType = (ComponentType) componentType.getSubtypes()[subPropertyIndex];
309+
}
303310
}
304-
}
305311

306-
loadedValue = componentType.getPropertyValue( loadedValue, componentType.getPropertyIndex( propertyName ) );
312+
loadedValue = componentType.getPropertyValue( loadedValue, componentType.getPropertyIndex( propertyName ) );
313+
}
314+
else {
315+
// Association is probably defined in an element collection, so we can't do orphan removals
316+
loadedValue = null;
317+
}
307318
}
308319

309320
// orphaned if the association was nulled (child == null) or receives a new value while the
@@ -538,7 +549,7 @@ private static void cascadeCollectionElements(
538549
itr.next(),
539550
elemType,
540551
style,
541-
null,
552+
collectionType.getRole().substring( collectionType.getRole().lastIndexOf('.') + 1 ),
542553
anything,
543554
isCascadeDeleteEnabled
544555
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.hibernate.test.orphan.elementcollection;
2+
3+
import java.util.Collections;
4+
5+
import org.hibernate.Session;
6+
import org.hibernate.SessionFactory;
7+
import org.hibernate.boot.Metadata;
8+
import org.hibernate.boot.MetadataSources;
9+
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
10+
import org.hibernate.testing.TestForIssue;
11+
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
12+
import org.hibernate.testing.transaction.TransactionUtil;
13+
import org.junit.Before;
14+
import org.junit.Test;
15+
16+
@TestForIssue(jiraKey = "HHH-14597")
17+
public class ElementCollectionOrphanTest extends BaseCoreFunctionalTestCase {
18+
19+
@Override
20+
protected String[] getMappings() {
21+
return new String[] { "orphan/elementcollection/student.hbm.xml" };
22+
}
23+
24+
@Test
25+
public void setCompositeElementTest() {
26+
TransactionUtil.doInHibernate(
27+
this::sessionFactory,
28+
session -> {
29+
EnrollableClass aClass = new EnrollableClass();
30+
aClass.setId("123");
31+
aClass.setName("Math");
32+
session.save(aClass);
33+
34+
Student aStudent = new Student();
35+
aStudent.setId("s1");
36+
aStudent.setFirstName("John");
37+
aStudent.setLastName("Smith");
38+
39+
EnrolledClassSeat seat = new EnrolledClassSeat();
40+
seat.setId("seat1");
41+
seat.setRow(10);
42+
seat.setColumn(5);
43+
44+
StudentEnrolledClass enrClass = new StudentEnrolledClass();
45+
enrClass.setEnrolledClass(aClass);
46+
enrClass.setClassStartTime(130);
47+
enrClass.setSeat(seat);
48+
aStudent.setEnrolledClasses(Collections.singleton(enrClass));
49+
session.save(aStudent);
50+
}
51+
);
52+
}
53+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.hibernate.test.orphan.elementcollection;
2+
3+
import javax.persistence.Column;
4+
import javax.persistence.Entity;
5+
import javax.persistence.Id;
6+
import javax.persistence.Table;
7+
8+
@Entity
9+
@Table(name = "ENROLLABLECLASS")
10+
public class EnrollableClass {
11+
12+
@Id
13+
@Column(name = "id")
14+
private String id;
15+
16+
@Column(name = "name")
17+
private String name;
18+
19+
public String getId() {
20+
return id;
21+
}
22+
23+
public void setId(String id) {
24+
this.id = id;
25+
}
26+
27+
public String getName() {
28+
return name;
29+
}
30+
31+
public void setName(String name) {
32+
this.name = name;
33+
}
34+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.hibernate.test.orphan.elementcollection;
2+
3+
public class EnrolledClassSeat {
4+
private String id;
5+
private int row;
6+
private int column;
7+
8+
public String getId() {
9+
return id;
10+
}
11+
12+
public void setId(String id) {
13+
this.id = id;
14+
}
15+
16+
public int getRow() {
17+
return row;
18+
}
19+
20+
public void setRow(int row) {
21+
this.row = row;
22+
}
23+
24+
public int getColumn() {
25+
return column;
26+
}
27+
28+
public void setColumn(int column) {
29+
this.column = column;
30+
}
31+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.hibernate.test.orphan.elementcollection;
2+
3+
import java.util.Set;
4+
5+
public class Student {
6+
7+
private String id;
8+
private String firstName;
9+
private String lastName;
10+
private Set< StudentEnrolledClass > enrolledClasses;
11+
12+
public String getId() {
13+
return id;
14+
}
15+
16+
public void setId(String id) {
17+
this.id = id;
18+
}
19+
20+
public String getFirstName() {
21+
return firstName;
22+
}
23+
24+
public void setFirstName(String firstName) {
25+
this.firstName = firstName;
26+
}
27+
28+
public String getLastName() {
29+
return lastName;
30+
}
31+
32+
public void setLastName(String lastName) {
33+
this.lastName = lastName;
34+
}
35+
36+
public Set<StudentEnrolledClass> getEnrolledClasses() {
37+
return enrolledClasses;
38+
}
39+
40+
public void setEnrolledClasses(Set<StudentEnrolledClass> enrolledClasses) {
41+
this.enrolledClasses = enrolledClasses;
42+
}
43+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.hibernate.test.orphan.elementcollection;
2+
3+
public class StudentEnrolledClass {
4+
private EnrollableClass enrolledClass;
5+
private int classStartTime;
6+
private EnrolledClassSeat seat;
7+
8+
public EnrollableClass getEnrolledClass() {
9+
return enrolledClass;
10+
}
11+
12+
public void setEnrolledClass(EnrollableClass enrolledClass) {
13+
this.enrolledClass = enrolledClass;
14+
}
15+
16+
public int getClassStartTime() {
17+
return classStartTime;
18+
}
19+
20+
public void setClassStartTime(int classStartTime) {
21+
this.classStartTime = classStartTime;
22+
}
23+
24+
public EnrolledClassSeat getSeat() {
25+
return seat;
26+
}
27+
28+
public void setSeat(EnrolledClassSeat seat) {
29+
this.seat = seat;
30+
}
31+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!DOCTYPE hibernate-mapping PUBLIC
4+
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
5+
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
6+
7+
<hibernate-mapping>
8+
<class name="org.hibernate.test.orphan.elementcollection.Student" table="STUDENT" lazy="true" >
9+
10+
<id name="id" column="id">
11+
<generator class="assigned"/>
12+
</id>
13+
14+
<property name="firstName" column="FIRSTNAME" />
15+
<property name="lastName" column="LASTNAME" />
16+
17+
<set name="enrolledClasses" table="STUDENT_CLASS" lazy="true" fetch="subselect" cascade="all">
18+
<key column="STUDENT_ID" />
19+
20+
<composite-element class="org.hibernate.test.orphan.elementcollection.StudentEnrolledClass" >
21+
<many-to-one class="org.hibernate.test.orphan.elementcollection.EnrollableClass" name="enrolledClass"
22+
column="ENROLLED_CLASS_ID" not-null="true" lazy="false" unique="false" cascade="none" />
23+
<property name="classStartTime" column="STARTTIME" />
24+
<many-to-one class="org.hibernate.test.orphan.elementcollection.EnrolledClassSeat" name="seat"
25+
column="ENROLLED_CLASS_SEAT_ID" not-null="true" lazy="false" unique="true" cascade="all-delete-orphan" />
26+
</composite-element>
27+
</set>
28+
</class>
29+
30+
<class name="org.hibernate.test.orphan.elementcollection.EnrollableClass" lazy="true" >
31+
32+
<id name="id" column="id">
33+
<generator class="assigned"/>
34+
</id>
35+
<property name="name" column="NAME" />
36+
</class>
37+
38+
<class name="org.hibernate.test.orphan.elementcollection.EnrolledClassSeat" lazy="true" >
39+
<id name="id" column="id">
40+
<generator class="assigned"/>
41+
</id>
42+
<property name="row" column="SEAT_ROW" />
43+
<property name="column" column="SEAT_COLUMN" />
44+
</class>
45+
</hibernate-mapping>

0 commit comments

Comments
 (0)