The error “Pure native scalar queries are not yet supported” is a NotYetImplementedException that Hibernate 3.x to 4.1.x threw at startup for every @NamedNativeQuery without a resultClass or a resultSetMapping. A scalar query returns plain values such as a count or a name instead of entities, and those old Hibernate versions had no code to register a scalar query as a named query.
We meet the error in legacy apps that keep reports or bulk updates as named native queries, for example a nightly job that counts orders per status with plain SQL. Hibernate 4.2.0.Final fixed it (HHH-4412), and Hibernate 6 and 7 run these queries without any extra mapping. In old projects that cannot upgrade, we add a @SqlResultSetMapping with @ColumnResult.
The following example is the startup error of an app on hibernate-entitymanager 4.1.12.Final and Java 25.
javax.persistence.PersistenceException: [PersistenceUnit: birds-broken] Unable to build EntityManagerFactory
at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:915)
at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:57)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:63)
...
Caused by: org.hibernate.cfg.NotYetImplementedException: Pure native scalar queries are not yet supported
at org.hibernate.cfg.annotations.QueryBinder.bindNativeQuery(QueryBinder.java:145)
at org.hibernate.cfg.AnnotationBinder.bindQueries(AnnotationBinder.java:338)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:548)
...
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1741)
Notice that the exception comes from QueryBinder.bindNativeQuery() while Hibernate builds the factory, so the app fails before any query runs. Next, we look at why the old binder rejects these queries, and then at the two fixes, a mapping for old versions and an upgrade.
1. Why Old Hibernate Rejects Scalar Named Native Queries
A named native query is plain SQL declared once on an entity with @NamedNativeQuery and run later by its name. Our example is a bird watching log with one entity, Sighting (species, location, count, seenOn). The following mapping is enough to stop Hibernate 4.1.
@Entity
@NamedNativeQuery(name = "Sighting.countBySpecies",
query = "select count(*) from Sighting where species = ?")
public class Sighting { ... }
The query never runs. Hibernate reads the annotation while it builds the EntityManagerFactory, and its QueryBinder registers each named native query with a description of the result. In 4.1, the binder knew two kinds of result.
- With resultClass, each row becomes an entity.
- With resultSetMapping, each row is mapped by a named @SqlResultSetMapping.
When both attributes were empty, the binder threw the exception without looking at the SQL. So a native UPDATE declared as a named query failed in the same way, even though it returns no rows at all.
@NamedNativeQuery(name = "Sighting.renameLocation",
query = "update Sighting set location = ? where location = ?")
// NotYetImplementedException: Pure native scalar queries are not yet supported
Hibernate 4.2 added the missing third branch, so a query with no class and no mapping now returns the raw column values.
![Two startup flows side by side. In Hibernate 3.6 to 4.1 the QueryBinder reads @NamedNativeQuery, checks resultClass, then resultSetMapping, and when both are empty throws NotYetImplementedException so the EntityManagerFactory never starts. In Hibernate 4.2 to 7.4 the same check falls through to a scalar query definition, the factory starts, and the query returns Object, Object[] or an update count](https://howtodoinjava.com/wp-content/uploads/2026/10/hibernate-native-scalar-queries-startup-flows-side-side-hibernate.png)
A plain em.createNativeQuery(sql) never had the problem, because the check ran only for named queries. We ran the same entity on several versions, and only the wrapper exception differs, because the 3.6.10 result is wrapped in Unable to configure EntityManagerFactory instead of Unable to build.
| Hibernate version | Named native query without resultClass or resultSetMapping |
|---|---|
| 3.6.10.Final | NotYetImplementedException at startup |
| 4.0.1.Final, 4.1.12.Final | NotYetImplementedException at startup |
| 4.2.0.Final, 4.3.11.Final | Starts, the query returns raw values |
| 7.4.11.Final | Starts, the query returns raw values |
2. How to Fix It
We have two options. Projects that must stay on Hibernate 4.1 or older add a mapping, and every other project upgrades and deletes the mapping.
2.1. Add a resultSetMapping With @ColumnResult (Hibernate 4.1 and Older)
A @SqlResultSetMapping with only @ColumnResult entries describes a scalar result, one value per listed column alias. Once the query references the mapping, the binder takes the resultSetMapping branch and the factory starts.
@NamedNativeQuery(name = "Sighting.countBySpecies",
query = "select count(*) from Sighting where species = ?")
@SqlResultSetMappings({
@SqlResultSetMapping(name = "totalMapping", columns = @ColumnResult(name = "total")),
@SqlResultSetMapping(name = "speciesTotalMapping",
columns = {@ColumnResult(name = "species"), @ColumnResult(name = "total")})
})
@NamedNativeQueries({
@NamedNativeQuery(name = "Sighting.countBySpecies",
query = "select count(*) as total from Sighting where species = ?",
resultSetMapping = "totalMapping"),
@NamedNativeQuery(name = "Sighting.totalsBySpecies",
query = "select species, sum(count) as total from Sighting group by species order by species",
resultSetMapping = "speciesTotalMapping")
})
Every column in @ColumnResult must match an alias in the SQL, so count(*) gets the alias total. On Hibernate 4.1.12 with H2 1.4.200, the two queries return a BigInteger and a list of Object[] rows.
Object robins = em.createNamedQuery("Sighting.countBySpecies")
.setParameter(1, "Robin")
.getSingleResult(); // 2 (a BigInteger)
List<?> totals = em.createNamedQuery("Sighting.totalsBySpecies")
.getResultList(); // [Heron, 1], [Robin, 5] (Object[] rows)
For a native UPDATE, the mapping is a dummy. The statement returns no columns, but the reference satisfies the binder, and executeUpdate() ignores the mapping.
@NamedNativeQuery(name = "Sighting.renameLocation",
query = "update Sighting set location = ? where location = ?",
resultSetMapping = "totalMapping")
int renamed = em.createNamedQuery("Sighting.renameLocation")
.setParameter(1, "River Walk")
.setParameter(2, "River Bend")
.executeUpdate(); // 2
2.2. Upgrade to Hibernate 7
The real fix is an upgrade. Hibernate 4.1 reached its end of life long ago, and from 4.2.0.Final on the binder accepts named native queries without a mapping. Hibernate 6 and 7 use jakarta.persistence instead of javax.persistence, so the imports change too. The upgraded entity on Hibernate 7.4 declares the queries without any mapping.
@Entity
@NamedNativeQuery(name = "Sighting.countBySpecies",
query = "select count(*) from Sighting where species = :species")
@NamedNativeQuery(name = "Sighting.totalsBySpecies",
query = "select species, sum(count) as total from Sighting group by species order by species")
@NamedNativeQuery(name = "Sighting.renameLocation",
query = "update Sighting set location = :newName where location = :oldName")
public class Sighting { ... }
The @NamedNativeQuery annotation is repeatable now, so we no longer need the @NamedNativeQueries wrapper. A query with one column returns the value itself, and a query with several columns returns an Object[] per row.
Object robins = em.createNamedQuery("Sighting.countBySpecies")
.setParameter("species", "Robin")
.getSingleResult(); // 2 (a Long)
// select count(*) from Sighting where species = ?
List<Object[]> totals = em.createNamedQuery("Sighting.totalsBySpecies", Object[].class)
.getResultList(); // [Heron, 1], [Kingfisher, 1], [Robin, 5]
int renamed = em.createNamedQuery("Sighting.renameLocation")
.setParameter("newName", "River Walk")
.setParameter("oldName", "River Bend")
.executeUpdate(); // 3
The @ColumnResult mappings from section 2.1 still work after the upgrade, so we can migrate first and delete them later.
2.3. Other Ways to Get Scalar Values in Hibernate 7
In Hibernate 7, resultClass and the second argument of createNativeQuery() also accept a basic Java type, not only an entity class. For example, a reporting screen that shows the last sighting date wants a LocalDate, not whatever type the JDBC driver reports. We pick one of the following forms when we want a specific Java type.
- We set a basic type as resultClass of a named query, such as LocalDate.class.
- We pass a basic type as the second argument of createNativeQuery(), such as Integer.class or String.class.
- We pass Tuple.class to read the values by column alias instead of by position.
- We set type on a @ColumnResult to convert one column of a mapping.
@NamedNativeQuery(name = "Sighting.lastSeen",
query = "select max(seenOn) from Sighting where species = :species",
resultClass = LocalDate.class)
LocalDate lastSeen = (LocalDate) em.createNamedQuery("Sighting.lastSeen")
.setParameter("species", "Robin")
.getSingleResult(); // 2026-09-22 (a LocalDate)
Integer total = (Integer) em.createNativeQuery("select count(*) from Sighting", Integer.class)
.getSingleResult(); // 4 (an Integer, not a Long)
List<?> species = em.createNativeQuery("select distinct species from Sighting order by species", String.class)
.getResultList(); // [Heron, Kingfisher, Robin]
Tuple tuple = (Tuple) em.createNativeQuery(
"select species, count, seenOn from Sighting where location = 'Lake Park'", Tuple.class)
.getSingleResult();
Object species = tuple.get("species"); // Robin
Object count = tuple.get("count"); // 3 (an Integer)
LocalDate seenOn = tuple.get("seenOn", LocalDate.class); // 2026-09-20
@NamedNativeQuery(name = "Sighting.totalsByLocation",
query = "select location, sum(count) as total from Sighting group by location order by location",
resultSetMapping = "LocationTotalMapping")
@SqlResultSetMapping(name = "LocationTotalMapping",
columns = {
@ColumnResult(name = "location"),
@ColumnResult(name = "total", type = Integer.class)})
List<Object[]> totals = em.createNamedQuery("Sighting.totalsByLocation", Object[].class)
.getResultList(); // [Lake Park, 3], [River Bend, 4], totals as Integer
Without a type, the Java class of each value follows the column type that the driver reports. The project asserts the following types on H2 2.5.252.
| SQL expression | Java type in Hibernate 7.4 |
|---|---|
| count(*), sum(count) | Long |
| avg(count) | Double |
| count, max(count) (an integer column) | Integer |
| species, min(species) | String |
| seenOn, max(seenOn) | LocalDate |
Entity results, DTO records, @ConstructorResult, joins and paging work the same way as in any other native query, and our @NamedNativeQuery guide from the first paragraph covers them in depth.
The complete project on GitHub runs every Hibernate 7.4 query in this article with 12 JUnit tests. Its legacy-hibernate-4.1 folder reproduces the error and the workaround on Hibernate 4.1.12 with 4 more tests (mvn -q compile exec:java, mvn test).
3. Native Scalar Query FAQs
3.1. Why Did count(*) Change From BigInteger to Long?
The same countBySpecies query returned a BigInteger on Hibernate 4.1.12 with H2 1.4.200 and returns a Long on Hibernate 7.4 with H2 2.5.252. The Dialect class of Hibernate 4 and 5 maps a BIGINT column of a scalar native query to BigInteger (5.6.15 returns a BigInteger too), while Hibernate 6.6, 7.0 and 7.4 return a Long. Code that casts the result to BigInteger throws a ClassCastException after the upgrade. We cast to Number when the code must run on both versions, or we pass the type we want.
long robins = ((Number) query.getSingleResult()).longValue(); // 2 on both versions
Long total = (Long) em.createNativeQuery("select count(*) from Sighting", Long.class)
.getSingleResult(); // 4 (a Long)
3.2. Does Hibernate’s Own @NamedNativeQuery Throw It Too?
Yes, on some versions. Hibernate also had its own org.hibernate.annotations.NamedNativeQuery, with extra attributes such as cacheable, and its binder kept the same check longer than the JPA one. We searched the QueryBinder class in the hibernate-core jars from Maven Central for the message.
| hibernate-core | javax.persistence.NamedNativeQuery | org.hibernate.annotations.NamedNativeQuery |
|---|---|---|
| 3.6.10 to 4.1.12 | Throws | Throws |
| 4.2.0 to 5.2.18 | Accepted | Throws |
| 5.3.36 and later | Accepted | Accepted |
So a project on Hibernate 4.2 to 5.2 that uses the Hibernate annotation still sees the error. We fix it either with the same resultSetMapping or by switching to the standard jakarta.persistence.NamedNativeQuery.

3.3. Can I Use a @NamedQuery Instead?
Yes, when the query does not need database-specific SQL. A JPQL named query such as select count(s) from Sighting s where s.species = :species never needed a mapping on any version, and Hibernate checks its syntax at startup. For bulk changes, we can read how native update statements interact with entities that are already loaded.
4. Conclusion
The error NotYetImplementedException: Pure native scalar queries are not yet supported comes from Hibernate 3.6 to 4.1, which could not register a named native query without resultClass or resultSetMapping, whether the SQL was a select or an update. On those versions, a @SqlResultSetMapping with @ColumnResult lets the factory start.
On Hibernate 4.2 and later, and on Hibernate 7.4 today, the same query returns either a single value or an Object[] without any mapping. After the upgrade we only watch the Java types, such as Long instead of BigInteger for count(*).
5. References
- HHH-4412: bulk update with native SQL queries (fixed in 4.2.0.Final)
- Jakarta Persistence 3.2 specification: SQL Queries
- NamedNativeQuery JavaDoc (Jakarta Persistence 3.2)
- ColumnResult JavaDoc (Jakarta Persistence 3.2)
- Hibernate ORM 7.4 User Guide: Scalar Queries
Happy Learning !!
org.hibernate.HibernateException: Could not resolve column name in result set [count]
i am getting following error when I use your code.
Hi,
Just wanted to say thanks. This solved my Problem (years later haha).
Just wanted to add that if the query returns 0.xx entity columns you could also specify a result class and the query would return a list of Entities with only the specified column filled.
Regards,
Kim