Lastig

初級Web系エンジニアの技術系とかもろもろのブログ

【エラー解決】org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ResultSet に列名 DTYPE は見つかりませんでした。

SpringとJPAを使って自作のアプリを作っていたらDaoで「ResultSet に列名 DTYPE は見つかりませんでした。」というエラーがでたので、原因と解決法をメモ。



サマリー



エラー内容
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ResultSet に列名 DTYPE は見つかりませんでした。

原因
Entityの継承関係。親クラス、子クラスの両方に@Entityをつけるなんてナシだよというおはなし。

解決策
継承元の親クラスEntityに@MappedSuperclassアノテーションだけつけて、それを継承した子クラスEntityに@Entityをつける。

用語
DTYPE・・・継承関係のあるクラスを区別するための識別子。




エラー内容


ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ResultSet に列名 DTYPE は見つかりませんでした。

DTYPEってなんだと思ってググる

JPA永続性プロバイダは、デフォルトで、継承階層でクラスを区別するためのDTYPEという識別子列を作成します。

※引用元:JPA注釈の参照情報
ということで原因はEntityの継承関係にありそう。というか実際そうだった。

英語では

the Persistence provider assumes a default column name of DTYPE and column type of DiscriminatorType.STRING.

とか書いてあるのでDiscriminatorTypeの略なのだろう。Discriminatorは識別という意味だそうだ。



対処法



解決策としては共通部分のEntityに@MappedSuperclassアノテーションをつけて、それを継承した2つのクラスを作る。

勝手な推測だけど、子クラスにも@Entityをつけると、hibernate.loaderがQueryを実行して得られるObjectに親クラスと子クラスの両方のEntityの型を関連づけてしまうので、どっちのEntityに変換すればいいのかわからなくなるみたいな感じ?



エラーが起きたソース



HogeEntity.java

@Entity
@Table(name="hoge")
public class HogeEntity {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	protected Long id;

	@Column
	private String name;

	// getter, setterは省略

}

HogeHogeEntity.java

@Entity // <--ココが原因!
public class HogeHogeEntity extends HogeEntity{

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	protected Long id;

	@Column
	private Long fuga_id;

	// getter, setterは省略

}

HogeDao.java

/**
 * Idでデータを取得
 * @return
 */
public Optional<HogeEntity> selectById(long id) {

	EntityManager manager = factory.createEntityManager();

	StringBuilder sqlBld = new StringBuilder();
	sqlBld.append("  SELECT ");
	sqlBld.append("  * ");
	sqlBld.append("  FROM hoge ");
	sqlBld.append("  WHERE ");
	sqlBld.append("  id = :id ");

	Query query = manager.createNativeQuery(sqlBld.toString(), HogeParentEntity.class);
	query.setParameter("id", id);

	@SuppressWarnings("unchecked")
	List<HogeEntity> list = query.getResultList(); // <--ここでエラー

	manager.close();

	Optional<HogeEntity> optEntity = Optional.ofNullable(list.isEmpty() ? null : list.get(0));

	return optEntity;

}

※Optionalで書いてあるのは、今回のエラーには関係ない。

ちなみにgetResultList()ではなく、getSingleResult()を使った場合は、
クエリー結果のObjectをEntityに変換する処理で、ClassCastExceptionが発生する。対処法は同じ。

Object result = query.getSingleResult();

if (result != null) {
    HogeParentEntity entity = (HogeParentEntity ) result; // ここでClassCastException
}
 



修正後のソース



HogeAbstractEntity.java

@MappedSuperclass // <--ココを修正!
public class HogeAbstractEntity {
        
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	protected Long id;

	@Column
	private String name;

	// getter, setterは省略
}

HogeEntity.java

@Entity
@Table(name="hoge")
public class HogeEntity extends HogeAbstractEntity {
        // 何も書かない。継承するだけ。
}

HogeHogeEntity.java

@Entity
public class HogeHogeEntity extends HogeAbstractEntity {
        @Column
	private Long fuga_id;

  // getter, setterは省略
}

共通部分は@MappedSuperclassアノテーションをつけて、切り出してあげる。
実際に使うEntityは@MappedSuperclassをつけたEntityを継承して実装するかたち。



DTYPEなんてわかりにくいエラーメッセージやめてほしいよね。