If you’ve spent any time with JPA and Hibernate, you’ve probably run into this cryptic runtime exception at least once:

java.lang.IllegalArgumentException: Type specified for TypedQuery [java.lang.Integer]
is incompatible with query return type [class java.lang.Long]

The frustrating part? Your code compiles perfectly. Your entity’s id field is declared as Integer. Everything looks right. And yet, at runtime, Hibernate blows up. Here’s exactly why this happens and how to fix it.

The Setup

Imagine you have a JPA entity like this:

@Entity
public class Deal {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String status;
// ... other fields
}

And somewhere in your repository or facade, you write a JPQL query to fetch a list of IDs:

TypedQuery<Integer> query = em.createQuery(
"SELECT d.id FROM Deal d WHERE d.status = :status",
Integer.class
);
query.setParameter("status", "WON");
List<Integer> ids = query.getResultList();

This compiles. It looks correct. But at runtime you get the Long is incompatible with Integer exception.

Why This Happens

The root cause is a disconnect between three layers: your Java entity, JPQL, and the underlying SQL/JDBC type system.

Layer 1 – Your Java entity: You declared private Integer id. Fine.

Layer 2 – The database: Most relational databases (MySQL, PostgreSQL, etc.) store auto-increment primary keys as BIGINT (64-bit), regardless of what your Java field says.

Layer 3 – Hibernate’s type resolution: When Hibernate executes SELECT d.id, it does not look at your Java field declaration to decide what type to return. It looks at what the JDBC driver reports back from the database – which is BIGINT, mapped to java.lang.Long.

So Hibernate tries to return a Long, but you told TypedQuery to expect Integer. Hibernate detects this mismatch at query creation time and throws before even hitting the database.

The key insight is this:

TypedQuery<T> is a compile-time generic. The actual type-checking happens at runtime, based on what the database returns – not what your Java entity declares.

Auto-boxing makes this worse because it gives you a false sense of safety. You might think “well, Integer and Long are both numbers, Java can auto-box between them.” It cannot. Auto-boxing only works between a primitive and its exact wrapper (int ↔︎ Integerlong ↔︎ Long). There is no implicit widening conversion between Integer and Long in the generics system.

The Fix

Option 1 – Match the type to what the database actually returns (recommended)

TypedQuery<Long> query = em.createQuery(
"SELECT d.id FROM Deal d WHERE d.status = :status",
Long.class
);
List<Long> ids = query.getResultList();
Set<Long> idSet = new HashSet<>(ids);

This is the cleanest fix. Let the query return what the database naturally gives you.

Option 2 – Cast in the query itself

TypedQuery<Integer> query = em.createQuery(
"SELECT CAST(d.id AS integer) FROM Deal d WHERE d.status = :status",
Integer.class
);

This works but is fragile — JPQL CAST support varies across providers and database dialects.

Option 3 – Use an untyped query and cast manually

Query query = em.createQuery(
"SELECT d.id FROM Deal d WHERE d.status = :status"
);
List<Object> raw = query.getResultList();
Set<Long> idSet = raw.stream()
.map(o -> ((Number) o).longValue())
.collect(Collectors.toSet());

This is the most defensive approach if you are unsure what type the driver will return, but it sacrifices type safety.

A Note on @GeneratedValue and Column Type

If you want your Java entity type and your query return type to genuinely match, the cleanest solution is to align your entity field with the database column:

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // matches BIGINT in the database

This way, TypedQuery<Long>, your entity field, and the database column are all in agreement. No surprises.

Summary

  • TypedQuery<T> type-checking happens at runtime, not compile time.
  • Hibernate resolves the return type from the JDBC/database layer, not your Java entity declaration.
  • BIGINT columns return Long in Java, even if your entity field says Integer.
  • Auto-boxing does not save you — there is no implicit Integer ↔︎ Long conversion in generics.
  • The safest fix: use Long for ID projections, and align your entity field type with the actual database column type.

This is one of those bugs that bites you exactly once and then you never forget it. Hopefully this saves you the debugging session.

Leave a comment