問題の内容
DBのテーブル定義をそのまま写したenum
で、予約語と定義が被ったため、_
を付けて定義しました。
問題の発生したenum
public enum Moge {
hoge("hoge"), fuga("fuga"), _native("native"); //nativeは予約語
/* 省略 */
ここで、Jdbc
へのクエリ結果をEnum.valueOf
を用いてマッピングしていましたが、DBから取れる値はnative
であり、_native
と異なるため、マッピングできませんでした。
対処
DBから取れる値に対応するフィールドを伝えるアノテーションを作り、リフレクションでアノテーションを処理することでマッピングできるようにしました。
MapByValue
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MapByValue {
String value();
}
アノテーションを付与した様子
public enum Moge {
hoge("hoge"), fuga("fuga"),
@MapByValue(value = "native") _native("native"); //nativeは予約語
/* 以下省略 */
アノテーションを処理する様子
@SuppressWarnings("unchecked")
private Object mapEnum(Class<?> clz, String columnValue) {
/* 省略 */
String finalColumnValue = columnValue;
columnValue = Arrays.stream(clz.getFields()) //フィールドを全部取得
.filter(it -> {
//MapByValueアノテーションが付与され、かつその値とcolmunValueが等しい場合
MapByValue mapByValue = it.getAnnotation(MapByValue.class);
return mapByValue != null && mapByValue.value().equals(finalColumnValue);
})
.findFirst()
.map(Field::getName) //付与されたフィールドの名前を使う
.orElse(finalColumnValue);
return clz.cast(Enum.valueOf((Class<Enum>) clz, columnValue));
}
@SuppressWarnings("rawtypes")
@Override
protected Object getColumnValue(ResultSet rs, int index, PropertyDescriptor pd) throws SQLException {
Class<?> clz = pd.getPropertyType(); //型情報の取得
/* 省略 */
if (clz.isEnum()) { //enumの場合
byte[] bytes = rs.getBytes(index);
if (isEmpty(bytes)) {
return null;
}
return mapEnum(clz, new String(bytes, Charset.forName("UTF-8")));
}
/* 省略 */
別の案
Jsonから変換する都合で値からenum
へ変換する関数を全てのenum
が持っていたため、リフレクションからこれを利用することも考えましたが、アノテーション振って処理した方が汎用性が高いと判断しました。
valueOf
をOverride
できれば一番よかったんですが……。