AttributeConverter for TemporalAccessors and Strings

辰
2 min readJun 29, 2020

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);
}
}

--

--