Skip to content

Commit 8c05a6b

Browse files
refactor: Add support for OffsetDateTime and LocalTime properties.
Recent drivers support them without issues and don’t need separate conversions. Closes #2706.
1 parent 4fc80a3 commit 8c05a6b

File tree

5 files changed

+108
-4
lines changed

5 files changed

+108
-4
lines changed

src/main/java/org/springframework/data/neo4j/core/convert/CypherTypes.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.time.LocalDate;
1919
import java.time.LocalDateTime;
2020
import java.time.LocalTime;
21+
import java.time.OffsetDateTime;
2122
import java.time.OffsetTime;
2223
import java.time.ZonedDateTime;
2324
import java.util.ArrayList;
@@ -32,7 +33,7 @@
3233

3334
/**
3435
* Conversions for all known Cypher types, directly supported by the driver. See
35-
* <a href="https://neo4j.com/docs/driver-manual/current/cypher-values/">Working with Cypher values</a>.
36+
* <a href="https://neo4j.com/docs/java-manual/current/cypher-workflow/#java-driver-type-mapping">Working with Cypher values</a>.
3637
*
3738
* @author Michael J. Simons
3839
* @since 6.0
@@ -56,6 +57,7 @@ final class CypherTypes {
5657
hlp.add(ConverterBuilder.reading(Value.class, byte[].class, Value::asByteArray).andWriting(Values::value));
5758
hlp.add(ConverterBuilder.reading(Value.class, LocalDate.class, Value::asLocalDate).andWriting(Values::value));
5859
hlp.add(ConverterBuilder.reading(Value.class, OffsetTime.class, Value::asOffsetTime).andWriting(Values::value));
60+
hlp.add(ConverterBuilder.reading(Value.class, OffsetDateTime.class, Value::asOffsetDateTime).andWriting(Values::value));
5961
hlp.add(ConverterBuilder.reading(Value.class, LocalTime.class, Value::asLocalTime).andWriting(Values::value));
6062
hlp.add(ConverterBuilder.reading(Value.class, ZonedDateTime.class, Value::asZonedDateTime).andWriting(Values::value));
6163
hlp.add(ConverterBuilder.reading(Value.class, LocalDateTime.class, Value::asLocalDateTime).andWriting(Values::value));

src/main/java/org/springframework/data/neo4j/repository/query/PartValidator.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import java.time.Instant;
1919
import java.time.LocalDate;
2020
import java.time.LocalDateTime;
21+
import java.time.LocalTime;
22+
import java.time.OffsetDateTime;
2123
import java.time.OffsetTime;
2224
import java.time.ZonedDateTime;
2325
import java.util.Arrays;
@@ -50,12 +52,12 @@ class PartValidator {
5052
/**
5153
* A set of the temporal types that are directly passable to the driver and support a meaningful comparison in a
5254
* temporal sense (after, before). See
53-
* <a href="See https://neo4j.com/docs/driver-manual/1.7/cypher-values/#driver-neo4j-type-system" />
55+
* <a href="https://neo4j.com/docs/driver-manual/1.7/cypher-values/#driver-neo4j-type-system" />
5456
*/
5557
private static final Set<Class<?>> COMPARABLE_TEMPORAL_TYPES;
5658
static {
5759
Set<Class<?>> hlp = new TreeSet<>(Comparator.comparing(Class::getName));
58-
hlp.addAll(Arrays.asList(LocalDate.class, OffsetTime.class, ZonedDateTime.class, LocalDateTime.class, Instant.class));
60+
hlp.addAll(Arrays.asList(LocalDate.class, OffsetTime.class, OffsetDateTime.class, LocalTime.class, ZonedDateTime.class, LocalDateTime.class, Instant.class));
5961
COMPARABLE_TEMPORAL_TYPES = Collections.unmodifiableSet(hlp);
6062
}
6163

src/test/java/org/springframework/data/neo4j/integration/imperative/RepositoryIT.java

+34
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import java.time.Instant;
2525
import java.time.LocalDate;
2626
import java.time.LocalDateTime;
27+
import java.time.LocalTime;
28+
import java.time.OffsetDateTime;
29+
import java.time.ZoneId;
2730
import java.time.ZoneOffset;
2831
import java.time.ZonedDateTime;
2932
import java.util.ArrayList;
@@ -36,6 +39,7 @@
3639
import java.util.Map;
3740
import java.util.Optional;
3841
import java.util.Set;
42+
import java.util.TimeZone;
3943
import java.util.UUID;
4044
import java.util.function.Consumer;
4145
import java.util.function.Function;
@@ -133,6 +137,7 @@
133137
import org.springframework.data.neo4j.integration.shared.common.KotlinPerson;
134138
import org.springframework.data.neo4j.integration.shared.common.LikesHobbyRelationship;
135139
import org.springframework.data.neo4j.integration.shared.common.MultipleLabels;
140+
import org.springframework.data.neo4j.integration.shared.common.OffsetTemporalEntity;
136141
import org.springframework.data.neo4j.integration.shared.common.OneToOneSource;
137142
import org.springframework.data.neo4j.integration.shared.common.OneToOneTarget;
138143
import org.springframework.data.neo4j.integration.shared.common.ParentNode;
@@ -4372,6 +4377,26 @@ void findByPropertyOnRelationshipWithPropertiesRelatedEntity(
43724377
}
43734378
}
43744379

4380+
@Test // GH-2706
4381+
void findByOffsetDateTimeShouldWork(@Autowired TemporalRepository temporalRepository) {
4382+
4383+
temporalRepository.deleteAll();
4384+
4385+
LocalDateTime fixedDateTime = LocalDateTime.of(2023, 1, 1, 21, 21, 0);
4386+
ZoneId europeBerlin = TimeZone.getTimeZone("Europe/Berlin").toZoneId();
4387+
OffsetDateTime v1 = OffsetDateTime.of(fixedDateTime, europeBerlin.getRules().getOffset(fixedDateTime));
4388+
LocalTime v2 = fixedDateTime.toLocalTime();
4389+
4390+
temporalRepository.save(new OffsetTemporalEntity(v1, v2));
4391+
temporalRepository.save(new OffsetTemporalEntity(v1.minusDays(2), v2.minusMinutes(2)));
4392+
4393+
assertThat(temporalRepository.findAllByProperty1After(v1)).isEmpty();
4394+
assertThat(temporalRepository.findAllByProperty2After(v2)).isEmpty();
4395+
4396+
assertThat(temporalRepository.findAllByProperty1After(v1.minusDays(1))).hasSize(1);
4397+
assertThat(temporalRepository.findAllByProperty2After(v2.minusMinutes(1))).hasSize(1);
4398+
}
4399+
43754400
/**
43764401
* The tests in this class ensure that in case of an inheritance scenario no DTO is projected but the extending class
43774402
* is used. If it wasn't the case, we wouldn't find the relationship nor the other attribute.
@@ -4701,6 +4726,15 @@ interface SameIdEntitiesRepository extends Neo4jRepository<SameIdProperty.PolEnt
47014726
interface EntityWithCustomIdAndDynamicLabelsRepository
47024727
extends Neo4jRepository<EntitiesWithDynamicLabels.EntityWithCustomIdAndDynamicLabels, String> {}
47034728

4729+
interface TemporalRepository extends
4730+
Neo4jRepository<OffsetTemporalEntity, UUID> {
4731+
4732+
List<OffsetTemporalEntity> findAllByProperty1After(OffsetDateTime aValue);
4733+
4734+
List<OffsetTemporalEntity> findAllByProperty2After(LocalTime aValue);
4735+
4736+
}
4737+
47044738
@SpringJUnitConfig(Config.class)
47054739
static abstract class IntegrationTestBase {
47064740

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2011-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.shared.common;
17+
18+
import java.time.LocalTime;
19+
import java.time.OffsetDateTime;
20+
import java.util.UUID;
21+
22+
import org.springframework.data.neo4j.core.schema.GeneratedValue;
23+
import org.springframework.data.neo4j.core.schema.Id;
24+
import org.springframework.data.neo4j.core.schema.Node;
25+
26+
/**
27+
* Using some offset temporal types.
28+
*
29+
* @author Michael J. Simons
30+
*/
31+
@Node
32+
public class OffsetTemporalEntity {
33+
34+
@Id
35+
@GeneratedValue
36+
private UUID uuid;
37+
38+
private OffsetDateTime property1;
39+
40+
private LocalTime property2;
41+
42+
public OffsetTemporalEntity(OffsetDateTime property1, LocalTime property2) {
43+
this.property1 = property1;
44+
this.property2 = property2;
45+
}
46+
47+
public UUID getUuid() {
48+
return uuid;
49+
}
50+
51+
public OffsetDateTime getProperty1() {
52+
return property1;
53+
}
54+
55+
public void setProperty1(OffsetDateTime property1) {
56+
this.property1 = property1;
57+
}
58+
59+
public LocalTime getProperty2() {
60+
return property2;
61+
}
62+
63+
public void setProperty2(LocalTime property2) {
64+
this.property2 = property2;
65+
}
66+
}

src/test/java/org/springframework/data/neo4j/repository/support/Neo4jRepositoryFactoryTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ void validateIgnoreCaseShouldWork() {
122122
void validateTemporalShouldWork() {
123123

124124
assertThatExceptionOfType(QueryCreationException.class).isThrownBy(() -> repositoryFactory.getRepository(InvalidTemporal.class))
125-
.withMessageMatching("Could not create query for .*: The keywords \\[IsAfter, After] work only with properties with one of the following types: \\[class java.time.Instant, class java.time.LocalDate, class java.time.LocalDateTime, class java.time.OffsetTime, class java.time.ZonedDateTime]");
125+
.withMessageMatching("Could not create query for .*: The keywords \\[IsAfter, After] work only with properties with one of the following types: \\[class java.time.Instant, class java.time.LocalDate, class java.time.LocalDateTime, class java.time.LocalTime, class java.time.OffsetDateTime, class java.time.OffsetTime, class java.time.ZonedDateTime]");
126126
}
127127

128128
@Test

0 commit comments

Comments
 (0)