DB検索結果がオブジェクトにマッピングされない
SpringbootでNativeQueryでDBにアクセスする時、Entityと素直にマッピングされない結果が欲しかったため、以下の方法で独自クラスにマッピング定義をして結果を格納するようにしました。
- 結果セット格納用POJO
@Data
@AllArgsConstructor
public class ForMapping {
private Long id,
private String name,
private Timestamp created
}
※コンストラクタやsetter/getterはlombokでやってます。
- Entityクラス
@SqlResultSetMapping(name = "MappingDef", classes =
{
@ConstructorResult(targetClass = ForMapping.class, columns =
{
@ColumnResult(name = "id", type = Long.class),
@ColumnResult(name = "name", type = String.class),
@ColumnResult(name = "created", type = Timestamp.class),
})
})
テーブル上は、idはlong、nameはTEXT、createdはTIMESTAMP型だったので、それぞれ対応するJavaのクラスを型に指定してマッピングを試みます。
これでSQLを実行して結果を取得しようとした所、結果が1件も取得できませんでした。地道にデバッグしてみると、SQLの実行自体は成功していましたが、結果セットのPOJOへのマッピングのあたりで失敗しているようでした。こんなエラーメッセージが。
Could not locate appropriate constructor on class
エラーメッセージはHibernateの以下の部分で出ていることが分かりました。やはり、SQLの結果セットをPOJOにマッピングするところのようです。
private static Constructor resolveConstructor(Class targetClass, List<Type> types) {
for ( Constructor constructor : targetClass.getConstructors() ) {
final Class[] argumentTypes = constructor.getParameterTypes();
if ( argumentTypes.length != types.size() ) {
continue;
}
boolean allMatched = true;
for ( int i = 0; i < argumentTypes.length; i++ ) {
if ( ! areAssignmentCompatible( argumentTypes[i], types.get( i ).getReturnedClass() ) ) {
allMatched = false;
break;
}
}
if ( !allMatched ) {
continue;
}
return constructor;
}
throw new IllegalArgumentException( "Could not locate appropriate constructor on class : " + targetClass.getName() );
}
原因はマッピング定義の型誤り
デバッグで分かりましたが原因は単純で、createdという項目をjava.sql.Timestamp型で指定しているのがNGだったようです。これにより、マッピングしようとしてもマッチするコンストラクタが見つからん!ということで、先のエラーメッセージでした。
公式の情報が見つけられなかったのですが、ネイティブクエリの結果セットをマッピングする時にDB上のTIMESTAMP型をjava.util.Dateにマッピングする、という情報を以下URL内で見つけました。
マッピングの定義部分とPOJOの型をjava.util.Dateに変更したところ、無事結果を取得できるようになりました。