Skip to content

Commit 434e458

Browse files
Develop-KIMclaude
authored andcommitted
HHH-20661 Return shared DateJavaType instances per temporal precision
DateJavaType#resolveTypeForPrecision created a new DateJavaType on every call, so the same java.util.Date reached through different attribute paths (e.g. an entity field vs. one inherited from a @MappedSuperclass) resolved to distinct JavaType instances. A union of two such columns was then rejected by SqmQueryGroup's identity comparison with "Select items of the same index must have the same java type across all query parts", even though the equivalent Criteria query worked. Follow the pattern used by the other temporal JavaTypes: override forDatePrecision/forTimePrecision/forTimestampPrecision to return shared per-precision instances instead of allocating a new one. Resolving the same precision now yields the same instance, so the existing identity comparison keeps working and genuinely different types are still rejected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d9b43eb commit 434e458

2 files changed

Lines changed: 113 additions & 4 deletions

File tree

hibernate-core/src/main/java/org/hibernate/type/descriptor/java/DateJavaType.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
*/
2727
public class DateJavaType extends AbstractTemporalJavaType<Date> implements VersionJavaType<Date> {
2828
public static final DateJavaType INSTANCE = new DateJavaType();
29+
@SuppressWarnings("deprecation")
30+
private static final DateJavaType DATE_INSTANCE = new DateJavaType( TemporalType.DATE );
31+
@SuppressWarnings("deprecation")
32+
private static final DateJavaType TIME_INSTANCE = new DateJavaType( TemporalType.TIME );
2933
private final @SuppressWarnings("deprecation") TemporalType precision;
3034

3135
public static class DateMutabilityPlan extends MutableMutabilityPlan<Date> {
@@ -92,10 +96,18 @@ public Date cast(Object value) {
9296
}
9397

9498
@Override
95-
public TemporalJavaType<Date> resolveTypeForPrecision(
96-
@SuppressWarnings("deprecation") TemporalType precision,
97-
TypeConfiguration typeConfiguration) {
98-
return precision == null ? this : new DateJavaType( precision );
99+
protected TemporalJavaType<Date> forDatePrecision(TypeConfiguration typeConfiguration) {
100+
return DATE_INSTANCE;
101+
}
102+
103+
@Override
104+
protected TemporalJavaType<Date> forTimePrecision(TypeConfiguration typeConfiguration) {
105+
return TIME_INSTANCE;
106+
}
107+
108+
@Override
109+
protected TemporalJavaType<Date> forTimestampPrecision(TypeConfiguration typeConfiguration) {
110+
return INSTANCE;
99111
}
100112

101113
@Override
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.orm.test.query.hql;
6+
7+
import java.util.Date;
8+
import java.util.List;
9+
10+
import org.hibernate.testing.orm.junit.DomainModel;
11+
import org.hibernate.testing.orm.junit.JiraKey;
12+
import org.hibernate.testing.orm.junit.SessionFactory;
13+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
14+
import org.junit.jupiter.api.Test;
15+
16+
import jakarta.persistence.Entity;
17+
import jakarta.persistence.Id;
18+
import jakarta.persistence.MappedSuperclass;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
22+
23+
@DomainModel(annotatedClasses = UnionSameJavaTypeDifferentPathTest.EntityA.class)
24+
@SessionFactory
25+
@JiraKey("HHH-20661")
26+
public class UnionSameJavaTypeDifferentPathTest {
27+
28+
@Test
29+
public void testUnionOfSameJavaTypeFromDifferentPaths(SessionFactoryScope scope) {
30+
scope.inTransaction( session -> {
31+
final List<Object[]> results = session.createQuery(
32+
"select a.code, a.eventDate as d from EntityA a " +
33+
"union " +
34+
"select a2.code, a2.createdAt as d from EntityA a2",
35+
Object[].class
36+
).list();
37+
assertThat( results ).isNotNull();
38+
} );
39+
}
40+
41+
@Test
42+
public void testUnionOfGenuinelyDifferentJavaTypesStillRejected(SessionFactoryScope scope) {
43+
scope.inTransaction( session -> assertThatThrownBy( () -> session.createQuery(
44+
"select a.code from EntityA a " +
45+
"union " +
46+
"select a2.eventDate from EntityA a2",
47+
Object.class
48+
).list() ).hasMessageContaining( "must have the same java type across all query parts" ) );
49+
}
50+
51+
@MappedSuperclass
52+
public static class BaseAuditable {
53+
private Date createdAt;
54+
55+
public Date getCreatedAt() {
56+
return createdAt;
57+
}
58+
59+
public void setCreatedAt(Date createdAt) {
60+
this.createdAt = createdAt;
61+
}
62+
}
63+
64+
@Entity(name = "EntityA")
65+
public static class EntityA extends BaseAuditable {
66+
@Id
67+
private Long id;
68+
69+
private String code;
70+
71+
private Date eventDate;
72+
73+
public Long getId() {
74+
return id;
75+
}
76+
77+
public void setId(Long id) {
78+
this.id = id;
79+
}
80+
81+
public String getCode() {
82+
return code;
83+
}
84+
85+
public void setCode(String code) {
86+
this.code = code;
87+
}
88+
89+
public Date getEventDate() {
90+
return eventDate;
91+
}
92+
93+
public void setEventDate(Date eventDate) {
94+
this.eventDate = eventDate;
95+
}
96+
}
97+
}

0 commit comments

Comments
 (0)