Native SQL when you need it
Drop to real SQL for what JPQL cannot express, without losing entity mapping.
Open this lesson in the learning hubKey points
createNativeQuery(sql, Book.class)runs raw SQL and hands back fully managed entities, as long as you select the mapped columns.- Use it for window functions, CTEs, upserts, full-text search - anything JPQL has no syntax for.
- Without an entity class you get
Object[]rows. Map them with@SqlResultSetMappingor a Spring Data interface projection. - Hibernate cannot tell which tables your SQL touches, so it flushes pending changes first. That is usually exactly what you want.
- Bulk
update/deleteskips the persistence context. Entities already loaded go stale - clear the context after one. - Native SQL ties that query to one database dialect. Fine, as long as it is a deliberate choice.
Example
// Raw SQL mapped back to managed Book entities
List<Book> best = em.createNativeQuery(
"select b.* from book b " +
"join review r on r.book_id = b.id " +
"group by b.id having avg(r.stars) >= :min " +
"order by avg(r.stars) desc limit 10", Book.class)
.setParameter("min", 4.5)
.getResultList();
// Spring Data: native SQL into a projection interface, no entities involved
public interface BookRepository extends JpaRepository<Book, Long> {
@Query(value = "select b.title as title, count(r.id) as reviews " +
"from book b left join review r on r.book_id = b.id " +
"group by b.title", nativeQuery = true)
List<TitleReviews> titleReviewCounts();
interface TitleReviews {
String getTitle();
long getReviews();
}
}
Native SQL is an escape hatch, not a defeat.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Hibernate course, and every lesson in it is listed on the Hibernate contents page.