Skip to content

Commit 18b1cea

Browse files
mbelladebeikov
authored andcommitted
HHH-17666 Fix trunc function argument type resolver
1 parent b1319ad commit 18b1cea

File tree

2 files changed

+132
-1
lines changed

2 files changed

+132
-1
lines changed

hibernate-core/src/main/java/org/hibernate/dialect/function/TruncFunction.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ public TruncFunction(
7272
"trunc",
7373
new TruncArgumentsValidator(),
7474
StandardFunctionReturnTypeResolvers.useArgType( 1 ),
75-
StandardFunctionArgumentTypeResolvers.ARGUMENT_OR_IMPLIED_RESULT_TYPE
75+
StandardFunctionArgumentTypeResolvers.byArgument(
76+
StandardFunctionArgumentTypeResolvers.IMPLIED_RESULT_TYPE,
77+
StandardFunctionArgumentTypeResolvers.NULL
78+
)
7679
);
7780
this.numericRenderingSupport = new TruncRenderingSupport(
7881
new PatternRenderer( truncPattern ),
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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.orm.test.query.hql;
8+
9+
import java.sql.Timestamp;
10+
import java.time.Instant;
11+
import java.time.ZoneOffset;
12+
import java.time.ZonedDateTime;
13+
import java.time.temporal.ChronoUnit;
14+
import java.util.Calendar;
15+
import java.util.Date;
16+
import java.util.GregorianCalendar;
17+
18+
import org.hibernate.dialect.DerbyDialect;
19+
20+
import org.hibernate.testing.orm.junit.DomainModel;
21+
import org.hibernate.testing.orm.junit.Jira;
22+
import org.hibernate.testing.orm.junit.SessionFactory;
23+
import org.hibernate.testing.orm.junit.SessionFactoryScope;
24+
import org.hibernate.testing.orm.junit.SkipForDialect;
25+
import org.junit.jupiter.api.AfterAll;
26+
import org.junit.jupiter.api.BeforeAll;
27+
import org.junit.jupiter.api.Test;
28+
29+
import jakarta.persistence.AttributeConverter;
30+
import jakarta.persistence.Convert;
31+
import jakarta.persistence.Converter;
32+
import jakarta.persistence.Entity;
33+
import jakarta.persistence.Id;
34+
35+
import static org.assertj.core.api.Assertions.assertThat;
36+
37+
/**
38+
* @author Marco Belladelli
39+
*/
40+
@DomainModel( annotatedClasses = TruncConvertedDatetimeAttributeTest.TestEntity.class )
41+
@SessionFactory
42+
@SkipForDialect( dialectClass = DerbyDialect.class, reason = "Derby doesn't support any form of date truncation" )
43+
@Jira( "https://hibernate.atlassian.net/browse/HHH-17666" )
44+
public class TruncConvertedDatetimeAttributeTest {
45+
private static final Date DATE = new GregorianCalendar( 2017, Calendar.JANUARY, 24 ).getTime();
46+
private static final Instant INSTANT = ZonedDateTime.of( 2020, 10, 15, 20, 34, 45, 0, ZoneOffset.UTC ).toInstant();
47+
48+
@BeforeAll
49+
public void setUp(SessionFactoryScope scope) {
50+
scope.inTransaction( session -> session.persist( new TestEntity( 1L, DATE.getTime(), INSTANT ) ) );
51+
}
52+
53+
@AfterAll
54+
public void tearDown(SessionFactoryScope scope) {
55+
scope.inTransaction( session -> session.createMutationQuery( "delete from TestEntity" ).executeUpdate() );
56+
}
57+
58+
@Test
59+
public void testTruncSelection(SessionFactoryScope scope) {
60+
scope.inSession( session -> {
61+
assertThat( session.createQuery(
62+
"select trunc(instantCol, minute) from TestEntity",
63+
Instant.class
64+
).getSingleResult() ).isEqualTo( INSTANT.truncatedTo( ChronoUnit.MINUTES ) );
65+
assertThat( session.createQuery(
66+
"select trunc(dateCol, month) from TestEntity",
67+
Long.class
68+
).getSingleResult() ).isEqualTo( new GregorianCalendar( 2017, Calendar.JANUARY, 1 ).getTime().getTime() );
69+
} );
70+
}
71+
72+
@Test
73+
public void testTruncComparison(SessionFactoryScope scope) {
74+
scope.inSession( session -> {
75+
assertThat( session.createQuery(
76+
"from TestEntity where trunc(instantCol, hour) < current_date",
77+
TestEntity.class
78+
).getResultList() ).hasSize( 1 );
79+
assertThat( session.createQuery(
80+
"from TestEntity where trunc(dateCol, year) < current_timestamp",
81+
TestEntity.class
82+
).getResultList() ).hasSize( 1 );
83+
} );
84+
}
85+
86+
@Entity( name = "TestEntity" )
87+
public static class TestEntity {
88+
@Id
89+
private Long id;
90+
91+
@Convert( converter = DateConverter.class )
92+
private Long dateCol;
93+
94+
@Convert( converter = InstantConverter.class )
95+
private Instant instantCol;
96+
97+
public TestEntity() {
98+
}
99+
100+
public TestEntity(Long id, Long dateCol, Instant instantCol) {
101+
this.id = id;
102+
this.dateCol = dateCol;
103+
this.instantCol = instantCol;
104+
}
105+
}
106+
107+
@Converter
108+
public static class DateConverter implements AttributeConverter<Long, Date> {
109+
public Date convertToDatabaseColumn(Long time) {
110+
return time == null ? null : new Date( time );
111+
}
112+
113+
public Long convertToEntityAttribute(Date date) {
114+
return date == null ? null : date.getTime();
115+
}
116+
}
117+
118+
@Converter
119+
public static class InstantConverter implements AttributeConverter<Instant, Timestamp> {
120+
public Timestamp convertToDatabaseColumn(Instant instant) {
121+
return instant == null ? null : Timestamp.from( instant );
122+
}
123+
124+
public Instant convertToEntityAttribute(Timestamp timestamp) {
125+
return timestamp == null ? null : timestamp.toInstant();
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)