I’ve encountered, quite a few times, into situations that I need to convert database strings into LocalDateTime
instances.
Let’s write an abstract class for that.
abstract class TemporalAccessorStringConverter<
T extends TemporalAccessor>
implements AttributeConverter<T, String> {
}
Two constructors are added.
protected TemporalAccessorConverter(
final Class<T> clazz,
final DateTimeFormatter formatter) {
super();
this.clazz = requireNonNull(clazz);
this.formatter = requireNonNull(formatter);
}protected TemporalAccessorConverter(
final Class<T> clazz,
final String pattern) {
this(clazz, ofPattern(requireNonNull(pattern)));
}protected final Class<T> clazz;protected final DateTimeFormatter formatter;
convertToDatabaseColumn
is easy.
@Override
public String convertToDatabaseColumn(final T attribute) {
if (attribute == null) {
return null;
}
return formatter.format(attribute);
}
We need to do some fancy jobs for convertToEntityAttribute
method.
The goal is to finding the static method looks like this.
T static parse(CharSequence, DateTimeFormatter)
Such as LocalDateTime#parse, LocalDate#parse, or LocalTime#parse.
Let’s prepare a synchronized `WeakHashMap` for caching those methods.
private static Map<Class<?>, MethodHandle> HANDLES
= synchronizedMap(new WeakHashMap<>());
And a method for accessing (or creating if not exist yet) the parse method.
private static MethodHandle parseHandle(
final Class<?> clazz) {
requireNonNull(clazz, "clazz is null");
return HANDLES.computeIfAbsent(clazz, k -> {
try {
return publicLookup().findStatic(
clazz, "parse",
methodType(clazz, CharSequence.class,
DateTimeFormatter.class));
} catch (final ReflectiveOperationException roe) {
throw new RuntimeException(roe);
}
});
}
Now our convertToEntityAttribute
method looks like this.
@Override
public T convertToEntityAttribute(final String dbData) {
if (dbData == null) {
return null;
}
try {
return clazz.cast(
parseHandle(clazz).invoke(dbData, formatter));
} catch (final Throwable t) {
throw new RuntimeException(t);
}
}
Here comes a concrete class converts LocalDateTime
and String
formatted uuuuMMddHHmmss
.
public class LocalDateTimeUuuuMmDdHhMmSsConverter
extends TemporalAccessorStringConverter<LocalDateTime> { private static final String PATTERN = "uuuuMMddHHmmss"; static final DateTimeFormatter FORMATTER = ofPattern(PATTERN); public LocalDateTimeUuuuMmDdHhMmSsConverter() {
super(LocalDateTime.class, FORMATTER);
}
}