Skip to content

Commit ad6f326

Browse files
kirmerzlikinmbellade
authored andcommitted
HHH-18516 - Ignore @AttributeOverride for map key when checking type of map value
1 parent d39ba13 commit ad6f326

File tree

5 files changed

+121
-2
lines changed

5 files changed

+121
-2
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionBinder.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,15 +2356,19 @@ private AnnotatedClassType annotatedElementType(
23562356
}
23572357
else {
23582358
//force in case of attribute override
2359-
final boolean attributeOverride = property.hasDirectAnnotationUsage( AttributeOverride.class )
2360-
|| property.hasDirectAnnotationUsage( AttributeOverrides.class );
2359+
final boolean attributeOverride = mappingDefinedAttributeOverrideOnElement(property);
23612360
// todo : force in the case of Convert annotation(s) with embedded paths (beyond key/value prefixes)?
23622361
return isEmbedded || attributeOverride
23632362
? EMBEDDABLE
23642363
: buildingContext.getMetadataCollector().getClassType( elementClass );
23652364
}
23662365
}
23672366

2367+
protected boolean mappingDefinedAttributeOverrideOnElement(MemberDetails property) {
2368+
return property.hasDirectAnnotationUsage( AttributeOverride.class )
2369+
|| property.hasDirectAnnotationUsage( AttributeOverrides.class );
2370+
}
2371+
23682372
static AnnotatedColumns createElementColumnsIfNecessary(
23692373
Collection collection,
23702374
AnnotatedColumns elementColumns,

hibernate-core/src/main/java/org/hibernate/boot/model/internal/MapBinder.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,26 @@ public void secondPass(java.util.Map<String, PersistentClass> persistentClasses)
109109
};
110110
}
111111

112+
@Override
113+
protected boolean mappingDefinedAttributeOverrideOnElement(MemberDetails property) {
114+
if ( property.hasDirectAnnotationUsage( AttributeOverride.class ) ) {
115+
return namedMapValue( property.getDirectAnnotationUsage( AttributeOverride.class ) );
116+
}
117+
if ( property.hasDirectAnnotationUsage( AttributeOverrides.class ) ) {
118+
final AttributeOverrides annotations = property.getDirectAnnotationUsage( AttributeOverrides.class );
119+
for ( AttributeOverride attributeOverride : annotations.value() ) {
120+
if ( namedMapValue( attributeOverride ) ) {
121+
return true;
122+
}
123+
}
124+
}
125+
return false;
126+
}
127+
128+
private boolean namedMapValue(AttributeOverride annotation) {
129+
return annotation.name().startsWith( "value." );
130+
}
131+
112132
private void makeOneToManyMapKeyColumnNullableIfNotInProperty(MemberDetails property) {
113133
final Map map = (Map) this.collection;
114134
if ( map.isOneToMany() && property.hasDirectAnnotationUsage( MapKeyColumn.class ) ) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package org.hibernate.orm.test.annotations.attributeoverride;
2+
3+
import java.time.LocalTime;
4+
import java.util.Map;
5+
6+
import org.hibernate.testing.orm.junit.DomainModel;
7+
import org.hibernate.testing.orm.junit.JiraKey;
8+
import org.hibernate.testing.orm.junit.SessionFactory;
9+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
10+
import org.junit.jupiter.api.Test;
11+
12+
import jakarta.persistence.metamodel.EntityType;
13+
import jakarta.persistence.metamodel.MapAttribute;
14+
15+
import static jakarta.persistence.metamodel.Type.PersistenceType.BASIC;
16+
import static jakarta.persistence.metamodel.Type.PersistenceType.EMBEDDABLE;
17+
import static org.assertj.core.api.Assertions.assertThat;
18+
19+
@DomainModel(
20+
annotatedClasses = {
21+
Schedule.class,
22+
Route.class
23+
}
24+
)
25+
@SessionFactory
26+
class MapAttributeOverrideTest {
27+
28+
@Test
29+
@JiraKey("HHH-18516")
30+
void testMapOfEmbeddableKeysWithAttributeOverridesAndBasicValues(SessionFactoryScope sessionFactoryScope) {
31+
sessionFactoryScope.inTransaction(session -> {
32+
EntityType<Schedule> scheduleType = session.getMetamodel().entity(Schedule.class);
33+
MapAttribute<? super Schedule, ?, ?> departuresMapAttribute = scheduleType.getMap("departures");
34+
// Presence of @AttributeOverride-s for the key should only affect the type of the key, but not the type of the value
35+
assertThat(departuresMapAttribute.getKeyType().getPersistenceType()).isEqualTo(EMBEDDABLE);
36+
assertThat(departuresMapAttribute.getElementType().getPersistenceType()).isEqualTo(BASIC);
37+
38+
session.persist(new Schedule(Map.of(
39+
new Route("Hamburg", "Vienna"), LocalTime.NOON,
40+
new Route("Warsaw", "Barcelona"), LocalTime.MIDNIGHT
41+
)));
42+
assertThat(session.createQuery("FROM Schedule s", Schedule.class).getResultCount()).isOne();
43+
});
44+
}
45+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.hibernate.orm.test.annotations.attributeoverride;
2+
3+
import jakarta.persistence.Embeddable;
4+
5+
@Embeddable
6+
public class Route {
7+
8+
private String origin;
9+
private String destination;
10+
11+
public Route() {
12+
}
13+
14+
public Route(String origin, String destination) {
15+
this.origin = origin;
16+
this.destination = destination;
17+
}
18+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package org.hibernate.orm.test.annotations.attributeoverride;
2+
3+
import java.time.LocalTime;
4+
import java.util.Map;
5+
6+
import jakarta.persistence.AttributeOverride;
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.ElementCollection;
9+
import jakarta.persistence.Entity;
10+
import jakarta.persistence.GeneratedValue;
11+
import jakarta.persistence.Id;
12+
13+
@Entity
14+
public class Schedule {
15+
16+
@Id
17+
@GeneratedValue
18+
private Long id;
19+
20+
@ElementCollection
21+
@Column(name = "time")
22+
@AttributeOverride(name = "key.origin", column = @Column(name = "orig"))
23+
@AttributeOverride(name = "key.destination", column = @Column(name = "dest"))
24+
private Map<Route, LocalTime> departures;
25+
26+
public Schedule() {
27+
}
28+
29+
public Schedule(Map<Route, LocalTime> departures) {
30+
this.departures = departures;
31+
}
32+
}

0 commit comments

Comments
 (0)