4.

Hibernate「unexpected token: as」HQL 予約語衝突の原因と対処

編集
この記事の要点
  • Hibernate / JPA で unexpected token: as は HQL / JPQL のパース失敗
  • 主因は SQL 予約語(user / order / group など)をテーブル名・カラム名にしている、または HQL なのにテーブル名で書いている
  • HQL は エンティティ参照FROM user_table u ではなく FROM User u
  • 対処: @Table(name="user") / @Column(name="order") でマッピング、HQL ではエンティティ名を使う
  • AS の使い所も限定的: SELECT 句のエイリアスFROM のエンティティエイリアスのみ
  • 回避不能なら createNativeQuery() でネイティブ SQL に切替(バックティック等で予約語をエスケープ)

このエラーの典型

Hibernate / JPA でクエリ実行時に次のような例外が出ます:

org.hibernate.QueryException: unexpected token: as [SELECT u FROM user as u]
    at org.hibernate.hql.internal.ast.QuerySyntaxException.convert(...)
    at org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(...)
    ...
Caused by: NoViableAltException(...)

# 旧 net.sf.hibernate 系
net.sf.hibernate.QueryException: unexpected token: as [from order o where o.id = ?]

HQL / JPQL の文法的に as というトークンが想定外の位置に出てきた、という意味です。

原因 1: HQL でテーブル名を直接書いている

HQL は エンティティクラス参照で書きます。SQL のようにテーブル名で書くと、Hibernate は「エンティティが見つからない → 予約語? → パース失敗」となります。

// ❌ NG: テーブル名で書いている
String hql = "SELECT u FROM user_table as u WHERE u.id = :id";
Query q = em.createQuery(hql);

// ✅ OK: エンティティ名で書く
String jpql = "SELECT u FROM User u WHERE u.id = :id";
Query q = em.createQuery(jpql, User.class);

// エンティティ定義側
@Entity
@Table(name = "user_table")  // DB 上のテーブル名
public class User {
    @Id
    @Column(name = "user_id")
    private Long id;
}

原因 2: 予約語が含まれている

テーブル名・カラム名が user / order / group / desc / key のような SQL 予約語の場合、Hibernate は HQL → SQL 変換時にエスケープせず生成し、結果として unexpected tokenSQLGrammarException が起きます。

予約語の例使ってしまうと起きる現象
userPostgreSQL で syntax error at or near "user"
orderORDER BY と被って unexpected token
groupGROUP BY と衝突
desc / ascソート方向と衝突
key / typeMySQL で You have an error in your SQL syntax

対処 1: @Table / @Column でエスケープ

@Entity
@Table(name = "`order`")          // MySQL バックティック
// @Table(name = "\"order\"")     // PostgreSQL ダブルクォート
public class OrderEntity {

    @Id
    @Column(name = "order_id")
    private Long id;

    @Column(name = "`group`")     // カラム名も予約語ならエスケープ
    private String group;
}

// Hibernate 標準のエスケープ記法(DB ベンダー非依存)
@Table(name = "\"order\"")        // Hibernate が DB に応じて変換

application.properties で 常にエスケープさせる設定もあります:

# Spring Boot
spring.jpa.properties.hibernate.globally_quoted_identifiers=true

# Hibernate 直設定
hibernate.globally_quoted_identifiers=true

対処 2: HQL の AS の正しい使い方

HQL / JPQL で AS が許される位置は限定的です:

// ✅ エンティティのエイリアス(AS は省略可)
"FROM User AS u"
"FROM User u"          // 同じ意味

// ✅ SELECT 句のカラムエイリアス
"SELECT u.name AS userName FROM User u"

// ❌ NG: JOIN の ON にカラムエイリアスを書く
"SELECT u FROM User u JOIN Order AS o ON u.id = o.userId"
// → JPQL 2.1 以降は JOIN ... ON 自体は OK だが、エイリアス位置に注意

// ❌ NG: テーブル名で書く
"FROM user_table AS u"

対処 3: ネイティブ SQL に逃がす

どうしても複雑な SQL を書きたい場合は createNativeQuery() でネイティブ SQL を使い、結果を Entity マッピングします:

@PersistenceContext
EntityManager em;

// ネイティブ SQL
List users = em.createNativeQuery(
    "SELECT * FROM `user` WHERE created_at > ?1",
    User.class
).setParameter(1, since).getResultList();

// Spring Data JPA でも @Query(nativeQuery = true)
public interface UserRepository extends JpaRepository {
    @Query(value = "SELECT * FROM `user` WHERE created_at > ?1", nativeQuery = true)
    List findRecent(LocalDateTime since);
}

デバッグの定石

# application.properties で実 SQL を出力
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

実際に投げられる SQL を見れば、予約語エスケープ漏れか HQL の文法ミスかすぐ判別できます。

FAQ

Q: 旧 net.sf.hibernate と現在の org.hibernate で違いは?
A: net.sf.hibernate は Hibernate 2 系(2006 以前)。基本同じ問題ですが、新規プロジェクトは Hibernate 6.x + JPA 標準を推奨。

Q: @Table の name に予約語を入れたくない
A: テーブル名を app_user / orders に変える DB 設計が王道です。

Q: JPQL と HQL は同じ?
A: JPQL は仕様、HQL は Hibernate 独自拡張。基本構文は共通で、HQL の方が機能が広い(WITH 句等)。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. ids for this class must be manually assigned before calling save()
  2. Number of positional parameter types (1 does not match number of positional parameters (2)
  3. net.sf.hibernate.MappingException: No persister for ~
  4. net.sf.hibernate.QueryException: unexpected token: as [~]
  5. net.sf.hibernate.MappingException: Error reading resource: ... .hbm.xml Server returned HTTP response code: 503 for URL: http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd Nested exception: Server returned HTTP response code: 503 for URL: http://hibernate.sourceforge.net/hibernate-mapping-3
  6. IllegalArgumentException occurred while calling setter of