Что такое @NamedQuery и @NamedNativeQuery в Hibernate?
@NamedQuery задаёт JPQL-запрос с именем на уровне сущности, который проверяется при старте приложения. @NamedNativeQuery — то же самое, но для нативного SQL. Оба кэшируются в SessionFactory и вызываются через em.createNamedQuery().
@NamedQuery
Аннотация @NamedQuery позволяет определить JPQL-запрос один раз на уровне класса сущности и переиспользовать его по имени. Ключевое преимущество — запрос парсится и валидируется при инициализации SessionFactory, а не при первом вызове. Синтаксическая ошибка в JPQL обнаруживается на старте приложения.
@Entity
@NamedQueries({
@NamedQuery(
name = "User.findByEmail",
query = "SELECT u FROM User u WHERE u.email = :email",
hints = @QueryHint(name = "org.hibernate.cacheable", value = "true")
),
@NamedQuery(
name = "User.findActiveByRole",
query = "SELECT u FROM User u WHERE u.active = true AND u.role = :role ORDER BY u.createdAt DESC"
)
})
public class User {
@Id @GeneratedValue
private Long id;
private String email;
private String role;
private boolean active;
private LocalDateTime createdAt;
}
Вызов через JPA EntityManager:
TypedQuery<User> query = em.createNamedQuery("User.findByEmail", User.class);
query.setParameter("email", "alice@example.com");
User user = query.getSingleResult();
Соглашение об именовании: EntityName.queryDescription — это конвенция Spring Data JPA, которая позволяет репозиторию автоматически находить named query по имени метода.
// Spring Data JPA автоматически связывает метод с @NamedQuery "User.findByEmail"
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email); // ищет @NamedQuery "User.findByEmail"
}
@NamedNativeQuery
Используется для нативных SQL-запросов, когда JPQL недостаточен — например, при использовании специфичных для БД функций, оконных функций, RETURNING, ON CONFLICT и т.п. Нативные запросы не проверяются при старте — ошибка проявится только при выполнении.
@Entity
@NamedNativeQuery(
name = "User.findTopByActivity",
query = "SELECT u.id, u.email, COUNT(l.id) as login_count " +
"FROM users u " +
"JOIN login_events l ON l.user_id = u.id " +
"WHERE l.created_at > NOW() - INTERVAL '30 days' " +
"GROUP BY u.id, u.email " +
"ORDER BY login_count DESC " +
"LIMIT :limit",
resultSetMapping = "UserActivityMapping"
)
@SqlResultSetMapping(
name = "UserActivityMapping",
columns = {
@ColumnResult(name = "id", type = Long.class),
@ColumnResult(name = "email", type = String.class),
@ColumnResult(name = "login_count", type = Long.class)
}
)
public class User { /* ... */ }
Вызов:
Query query = em.createNamedQuery("User.findTopByActivity");
query.setParameter("limit", 10);
List<Object[]> results = query.getResultList();
Если результат должен маппиться на сущность целиком, достаточно указать resultClass:
@NamedNativeQuery(
name = "User.findByCountry",
query = "SELECT * FROM users WHERE country_code = :code",
resultClass = User.class
)
Named Queries в отдельном XML-файле
Для разделения запросов и кода их можно вынести в orm.xml:
<named-query name="User.findByEmail">
<query>SELECT u FROM User u WHERE u.email = :email</query>
</named-query>
Подводные камни
@NamedQueryвалидируется при старте только если Hibernate может построить метамодель — с некоторыми custom типами валидация может быть неполной.@NamedNativeQueryне валидируется при старте и не переносится автоматически между СУБД из-за диалектных отличий SQL.- Дублирование имён в
@NamedQueryвызываетHibernateExceptionпри старте — имена должны быть уникальны в рамках persistence unit. @SqlResultSetMappingдля@NamedNativeQueryс DTO-проекциями требует точного совпадения имён столбцов с псевдонимами в SQL — регистр важен.- Spring Data JPA ищет
@NamedQueryпо паттернуClassName.methodName— несоответствие имени ведёт к тихому игнорированию и генерации запроса из имени метода. - Named Queries не поддерживают динамические условия — для фильтрации с опциональными параметрами лучше использовать Criteria API или Spring Data Specifications.
- Кэширование результатов через
org.hibernate.cacheable=trueтребует включённого query cache, иначе hint молча игнорируется.
Common mistakes
- Путать термин «named queries» с соседним механизмом Hibernate.
- Не называть границу lifecycle, transaction, thread или request для «named queries».
- Игнорировать production-эффекты «named queries»: latency, SQL shape, memory, security или observability.
What the interviewer is testing
- Попросить объяснить механизм «named queries» на минимальном примере.
- Проверить, видит ли кандидат failure mode и диагностику для «named queries».
- Уточнить, какие настройки или API меняют «named queries» в реальном сервисе.