Skip to content

Commit 9bbf9d8

Browse files
tim-aerotfaulkes
andauthored
Add mappers for java.time classes (#131)
* Added support for LocalDate and LocalDateTime * Support for LocalDate and LocalDateTime - Added a unit test for LocalDateMapper and LocalDateTimeMapper - Changed LocalDateMapper to use EpochDays - Changed LocalDateTimeMapper to preserve full precision of the LocalDateTime down the nanosecond level. * Added LocalTime mapper Added a mapper for LocalTime to round out Java 8 date / time classes. Included a unit test for them. Co-authored-by: Tim Faulkes <[email protected]>
1 parent 403add8 commit 9bbf9d8

File tree

5 files changed

+231
-16
lines changed

5 files changed

+231
-16
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.aerospike.mapper.tools.mappers;
2+
3+
import java.time.LocalDate;
4+
5+
import com.aerospike.mapper.tools.TypeMapper;
6+
7+
public class LocalDateMapper extends TypeMapper {
8+
9+
@Override
10+
public Object toAerospikeFormat(Object value) {
11+
if (value == null) {
12+
return null;
13+
}
14+
return ((LocalDate)value).toEpochDay();
15+
}
16+
17+
@Override
18+
public Object fromAerospikeFormat(Object value) {
19+
if (value == null) {
20+
return null;
21+
}
22+
return LocalDate.ofEpochDay((Long) value);
23+
}
24+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.aerospike.mapper.tools.mappers;
2+
3+
import java.time.LocalDate;
4+
import java.time.LocalDateTime;
5+
import java.time.LocalTime;
6+
import java.util.Arrays;
7+
import java.util.List;
8+
9+
import com.aerospike.mapper.tools.TypeMapper;
10+
11+
/**
12+
* Map a java.time.LocalDateTime to Aerospike.
13+
* <p/>
14+
* If we store the data in a single long we can only store to the millisecond
15+
* precision, like:
16+
* <pre>
17+
* return Date.from(((LocalDateTime) value).toInstant(ZoneOffset.UTC)).getTime();
18+
* </pre>
19+
* Whereas LocalDateTime can store down to the nanosecond precision. To store this properly
20+
* we will split it into date and time components and store both in a list.
21+
* *
22+
* @author tfaulkes
23+
*
24+
*/
25+
public class LocalDateTimeMapper extends TypeMapper {
26+
27+
@Override
28+
public Object toAerospikeFormat(Object value) {
29+
if (value == null) {
30+
return null;
31+
}
32+
LocalDateTime dateTime = (LocalDateTime)value;
33+
LocalDate date = dateTime.toLocalDate();
34+
LocalTime time = dateTime.toLocalTime();
35+
return Arrays.asList(date.toEpochDay(), time.toNanoOfDay());
36+
}
37+
38+
@Override
39+
public Object fromAerospikeFormat(Object value) {
40+
if (value == null) {
41+
return null;
42+
}
43+
@SuppressWarnings("unchecked")
44+
List<Long> values = (List<Long>)value;
45+
LocalDate date = LocalDate.ofEpochDay(values.get(0));
46+
LocalTime time = LocalTime.ofNanoOfDay(values.get(1));
47+
return LocalDateTime.of(date, time);
48+
}
49+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.aerospike.mapper.tools.mappers;
2+
3+
import java.time.LocalTime;
4+
5+
import com.aerospike.mapper.tools.TypeMapper;
6+
7+
public class LocalTimeMapper extends TypeMapper {
8+
9+
@Override
10+
public Object toAerospikeFormat(Object value) {
11+
if (value == null) {
12+
return null;
13+
}
14+
return ((LocalTime)value).toNanoOfDay();
15+
}
16+
17+
@Override
18+
public Object fromAerospikeFormat(Object value) {
19+
if (value == null) {
20+
return null;
21+
}
22+
return LocalTime.ofNanoOfDay((Long) value);
23+
}
24+
}

src/main/java/com/aerospike/mapper/tools/utils/TypeUtils.java

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,20 @@
11
package com.aerospike.mapper.tools.utils;
22

3+
import java.lang.annotation.Annotation;
4+
import java.lang.reflect.Field;
5+
import java.lang.reflect.Method;
6+
import java.lang.reflect.ParameterizedType;
7+
import java.lang.reflect.Type;
8+
import java.time.Instant;
9+
import java.time.LocalDate;
10+
import java.time.LocalDateTime;
11+
import java.time.LocalTime;
12+
import java.util.Arrays;
13+
import java.util.Date;
14+
import java.util.List;
15+
import java.util.Map;
16+
import java.util.concurrent.ConcurrentHashMap;
17+
318
import com.aerospike.client.AerospikeException;
419
import com.aerospike.client.cdt.ListReturnType;
520
import com.aerospike.client.cdt.MapReturnType;
@@ -16,21 +31,26 @@
1631
import com.aerospike.mapper.tools.configuration.ClassConfig;
1732
import com.aerospike.mapper.tools.configuration.EmbedConfig;
1833
import com.aerospike.mapper.tools.configuration.ReferenceConfig;
19-
import com.aerospike.mapper.tools.mappers.*;
34+
import com.aerospike.mapper.tools.mappers.ArrayMapper;
35+
import com.aerospike.mapper.tools.mappers.BooleanMapper;
36+
import com.aerospike.mapper.tools.mappers.ByteMapper;
37+
import com.aerospike.mapper.tools.mappers.CharacterMapper;
38+
import com.aerospike.mapper.tools.mappers.DateMapper;
39+
import com.aerospike.mapper.tools.mappers.DefaultMapper;
40+
import com.aerospike.mapper.tools.mappers.EnumMapper;
41+
import com.aerospike.mapper.tools.mappers.FloatMapper;
42+
import com.aerospike.mapper.tools.mappers.InstantMapper;
43+
import com.aerospike.mapper.tools.mappers.IntMapper;
44+
import com.aerospike.mapper.tools.mappers.ListMapper;
45+
import com.aerospike.mapper.tools.mappers.LocalDateMapper;
46+
import com.aerospike.mapper.tools.mappers.LocalDateTimeMapper;
47+
import com.aerospike.mapper.tools.mappers.LocalTimeMapper;
48+
import com.aerospike.mapper.tools.mappers.MapMapper;
49+
import com.aerospike.mapper.tools.mappers.ObjectEmbedMapper;
50+
import com.aerospike.mapper.tools.mappers.ObjectReferenceMapper;
51+
import com.aerospike.mapper.tools.mappers.ShortMapper;
2052
import com.aerospike.mapper.tools.virtuallist.ReturnType;
2153

22-
import java.lang.annotation.Annotation;
23-
import java.lang.reflect.Field;
24-
import java.lang.reflect.Method;
25-
import java.lang.reflect.ParameterizedType;
26-
import java.lang.reflect.Type;
27-
import java.time.Instant;
28-
import java.util.Arrays;
29-
import java.util.Date;
30-
import java.util.List;
31-
import java.util.Map;
32-
import java.util.concurrent.ConcurrentHashMap;
33-
3454
public class TypeUtils {
3555

3656
private static final Map<Class<?>, TypeMapper> mappers = new ConcurrentHashMap<>();
@@ -65,9 +85,14 @@ private static TypeMapper getMapper(Class<?> clazz, AnnotatedType type, IBaseAer
6585
boolean addToMap = true;
6686
if (typeMapper == null) {
6787
if (Date.class.isAssignableFrom(clazz)) {
68-
typeMapper = new DateMapper();
69-
}
70-
if (Instant.class.isAssignableFrom(clazz)) {
88+
typeMapper = new DateMapper();
89+
} else if (LocalDateTime.class.isAssignableFrom(clazz)) {
90+
typeMapper = new LocalDateTimeMapper();
91+
} else if (LocalDate.class.isAssignableFrom(clazz)) {
92+
typeMapper = new LocalDateMapper();
93+
} else if (LocalTime.class.isAssignableFrom(clazz)) {
94+
typeMapper = new LocalTimeMapper();
95+
} else if (Instant.class.isAssignableFrom(clazz)) {
7196
typeMapper = new InstantMapper();
7297
} else if (Byte.class.isAssignableFrom(clazz) || Byte.TYPE.isAssignableFrom(clazz)) {
7398
typeMapper = new ByteMapper();
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package com.aerospike.mapper;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
6+
import java.time.LocalDate;
7+
import java.time.LocalDateTime;
8+
import java.time.LocalTime;
9+
import java.util.ArrayList;
10+
import java.util.List;
11+
12+
import org.junit.jupiter.api.Test;
13+
14+
import com.aerospike.client.Key;
15+
import com.aerospike.mapper.annotations.AerospikeEmbed;
16+
import com.aerospike.mapper.annotations.AerospikeKey;
17+
import com.aerospike.mapper.annotations.AerospikeRecord;
18+
import com.aerospike.mapper.tools.AeroMapper;
19+
20+
public class LocalDateConverterTest extends AeroMapperBaseTest {
21+
22+
@AerospikeRecord(namespace = "test", set = "local")
23+
public static class LocalDateTimeContainer {
24+
@AerospikeKey
25+
public int id;
26+
public LocalTime localTime;
27+
public LocalDate localDate;
28+
public LocalDateTime localDateTime;
29+
}
30+
31+
@AerospikeRecord(namespace = "test", set = "localNest")
32+
public static class LocalDateTimeTester {
33+
@AerospikeKey
34+
public int id = 1;
35+
@AerospikeEmbed
36+
public LocalDateTimeContainer cont;
37+
@AerospikeEmbed
38+
public List<LocalDateTimeContainer> contList;
39+
}
40+
41+
@Test
42+
public void testLocalDateTypes() {
43+
AeroMapper mapper = new AeroMapper.Builder(client).build();
44+
45+
LocalDateTimeContainer container = new LocalDateTimeContainer();
46+
container.id = 1;
47+
container.localDate = LocalDate.now();
48+
container.localDateTime = LocalDateTime.now();
49+
container.localTime = LocalTime.now();
50+
51+
mapper.save(container);
52+
53+
LocalDateTimeContainer readContainer = mapper.read(LocalDateTimeContainer.class, 1);
54+
assertEquals(container.id, readContainer.id);
55+
assertEquals(container.localDate, readContainer.localDate);
56+
assertEquals(container.localTime, readContainer.localTime);
57+
assertEquals(container.localDateTime, readContainer.localDateTime);
58+
59+
com.aerospike.client.Record record = client.get(null, new Key("test", "local", 1));
60+
long value = record.getLong("localDate");
61+
long timeValue = record.getLong("localTime");
62+
List<Long> dateTimeValues = (List<Long>) record.getList("localDateTime");
63+
assertTrue(value > 0);
64+
assertTrue(dateTimeValues.get(0) > 0);
65+
}
66+
67+
@Test
68+
public void testEmbeddedLocalDateTypes() {
69+
AeroMapper mapper = new AeroMapper.Builder(client).build();
70+
71+
LocalDateTimeContainer container = new LocalDateTimeContainer();
72+
container.id = 10;
73+
container.localDate = LocalDate.now();
74+
container.localDateTime = LocalDateTime.now();
75+
76+
LocalDateTimeTester tester = new LocalDateTimeTester();
77+
tester.id = 1;
78+
tester.cont = container;
79+
tester.contList = new ArrayList<LocalDateTimeContainer>();
80+
tester.contList.add(container);
81+
mapper.save(tester);
82+
83+
LocalDateTimeTester readTester = mapper.read(LocalDateTimeTester.class, 1);
84+
assertEquals(tester.id, readTester.id);
85+
assertEquals(tester.cont.localDate, readTester.cont.localDate);
86+
assertEquals(tester.cont.localDateTime, readTester.cont.localDateTime);
87+
88+
assertEquals(tester.id, readTester.id);
89+
assertEquals(tester.contList.get(0).localDate, readTester.contList.get(0).localDate);
90+
assertEquals(tester.contList.get(0).localDateTime, readTester.contList.get(0).localDateTime);
91+
92+
}
93+
}

0 commit comments

Comments
 (0)