Skip to content

Commit 509a0cc

Browse files
committed
HHH-19056 prevent NPE when using @mapsid on an embeddable
1 parent 0f41601 commit 509a0cc

File tree

2 files changed

+111
-2
lines changed

2 files changed

+111
-2
lines changed

hibernate-core/src/main/java/org/hibernate/id/CompositeNestedGeneratedValueGenerator.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.hibernate.type.CompositeType;
1919

2020
import static org.hibernate.generator.EventType.INSERT;
21+
import static org.hibernate.internal.util.ReflectHelper.getDefaultSupplier;
2122

2223
/**
2324
* For composite identifiers, defines a number of "nested" generations that
@@ -131,9 +132,12 @@ public void addGeneratedValuePlan(GenerationPlan plan) {
131132

132133
@Override
133134
public Object generate(SharedSessionContractImplementor session, Object object) {
134-
final Object context = generationContextLocator.locateGenerationContext( session, object );
135+
Object context = generationContextLocator.locateGenerationContext( session, object );
136+
if ( context == null ) {
137+
context = getDefaultSupplier( compositeType.getReturnedClass() ).get();
138+
}
135139
final List<Object> generatedValues = generatedValues( session, object, context );
136-
if ( generatedValues != null) {
140+
if ( generatedValues != null ) {
137141
final Object[] values = compositeType.getPropertyValues( context );
138142
for ( int i = 0; i < generatedValues.size(); i++ ) {
139143
values[generationPlans.get( i ).getPropertyIndex()] = generatedValues.get( i );
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.annotations.mapsid;
6+
7+
import jakarta.persistence.CascadeType;
8+
import jakarta.persistence.EmbeddedId;
9+
import jakarta.persistence.Entity;
10+
import jakarta.persistence.GeneratedValue;
11+
import jakarta.persistence.Id;
12+
import jakarta.persistence.ManyToOne;
13+
import jakarta.persistence.MapsId;
14+
import jakarta.persistence.OneToMany;
15+
import org.hibernate.testing.orm.junit.DomainModel;
16+
import org.hibernate.testing.orm.junit.Jira;
17+
import org.hibernate.testing.orm.junit.SessionFactory;
18+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
19+
import org.junit.jupiter.api.Test;
20+
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
import java.util.Objects;
24+
25+
26+
@SessionFactory
27+
@DomainModel(
28+
annotatedClasses = {
29+
MapsEmbeddedIdNullTest.Level0.class,
30+
MapsEmbeddedIdNullTest.Level1.class,
31+
MapsEmbeddedIdNullTest.Level2.class
32+
})
33+
@Jira("https://hibernate.atlassian.net/browse/HHH-19056")
34+
public class MapsEmbeddedIdNullTest {
35+
36+
@Test
37+
void test(SessionFactoryScope scope) {
38+
scope.inTransaction( s -> {
39+
Level0 level0 = new Level0();
40+
Level2 level2 = new Level2();
41+
Level1 level1 = new Level1( level0, level2 );
42+
level0.level1s.add( level1 );
43+
s.persist( level0 );
44+
} );
45+
}
46+
47+
@Entity(name = "Level0")
48+
public static class Level0 {
49+
@Id
50+
@GeneratedValue
51+
private Integer id;
52+
@OneToMany(mappedBy = "level0", cascade = CascadeType.ALL)
53+
private List<Level1> level1s = new ArrayList<>();
54+
}
55+
56+
@Entity(name = "Level1")
57+
public static class Level1 {
58+
@EmbeddedId
59+
Level1PK id;
60+
@MapsId("level0Id")
61+
@ManyToOne
62+
private Level0 level0;
63+
@MapsId("level2Id")
64+
@ManyToOne(cascade = CascadeType.ALL)
65+
private Level2 level2;
66+
67+
public Level1() {
68+
}
69+
70+
public Level1(Level0 level0, Level2 level2) {
71+
super();
72+
this.level0 = level0;
73+
this.level2 = level2;
74+
}
75+
}
76+
77+
@Entity(name = "Level2")
78+
public static class Level2 {
79+
@Id
80+
@GeneratedValue
81+
private Integer id;
82+
}
83+
84+
public static class Level1PK {
85+
private Integer level0Id;
86+
private Integer level2Id;
87+
88+
@Override
89+
public final boolean equals(Object o) {
90+
if ( !(o instanceof Level1PK level1PK) ) {
91+
return false;
92+
}
93+
94+
return Objects.equals( level0Id, level1PK.level0Id )
95+
&& Objects.equals( level2Id, level1PK.level2Id );
96+
}
97+
98+
@Override
99+
public int hashCode() {
100+
int result = Objects.hashCode( level0Id );
101+
result = 31 * result + Objects.hashCode( level2Id );
102+
return result;
103+
}
104+
}
105+
}

0 commit comments

Comments
 (0)