From e76d1e57526c47e26228f7bbdc20750d68547412 Mon Sep 17 00:00:00 2001 From: Gavin King Date: Tue, 27 Jun 2023 14:58:01 +0200 Subject: [PATCH] document some more things about criteria queries and names queries --- .../asciidoc/introduction/Interacting.adoc | 59 ++++++++++++++++++- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/documentation/src/main/asciidoc/introduction/Interacting.adoc b/documentation/src/main/asciidoc/introduction/Interacting.adoc index e15f9ae16d..5b55f37475 100644 --- a/documentation/src/main/asciidoc/introduction/Interacting.adoc +++ b/documentation/src/main/asciidoc/introduction/Interacting.adoc @@ -526,11 +526,12 @@ Hibernate features three complementary ways to write queries: :hql: {userGuideBase}#query-language A full discussion of the query language would require just as much text as the rest of this Introduction. -Fortunately, HQL is already described in exhaustive (and exhausting) detail in the {hql}[User Guide]. +Fortunately, HQL is already described in exhaustive (and exhausting) detail in _A guide to Hibernate Query Language 6_. +It doesn't make sense to repeat that information here. // The query language is discussed in great detail below in <>. Here we want to see how to execute a query via the `Session` or `EntityManager` API. -The method we call depends on what Kind it is: +The method we call depends on what kind of query it is: - _selection queries_ return a result list, but do not modify the data, but - _mutation queries_ modify data, and return the number of modified rows. @@ -734,6 +735,30 @@ List matchingBooks = .getResultList(); ---- +Update, insert, and delete queries work similarly: + +[source,java] +---- +CriteriaDelete delete = cb.createCriteriaDelete(Book.class); +Root book = delete.from(Book.class); +delete.where(cb.lt(cb.year(book.get(Book_.publicationDate)), 2000)); +session.createMutationQuery(delete).executeUpdate(); +---- + +[TIP] +==== +It's even possible to transform a HQL query string to a criteria query, and modify the query programmatically before execution: +[source,java] +---- +HibernateCriteriaBuilder cb = sessionFactory.getCriteriaBuilder(); +var query = cb.createQuery("from Book where year(publicationDate) > 2000", Book.class); +var root = (Root) query.getRootList().get(0); +query.where(cb.like(root.get(Book_.title), cb.literal("Hibernate%"))); +query.orderBy(cb.asc(root.get(Book_.title)), cb.desc(root.get(Book_.isbn))); +List matchingBooks = session.createSelectionQuery(query).getResultList(); +---- +==== + When all else fails, and sometimes even before that, we're left with the option of writing a query in SQL. [[native-queries]] @@ -921,7 +946,7 @@ Now, this is only _superficially_ more typesafe, since the query itself is not c But perhaps you find it more aesthetically pleasing. And if we're going to be passing query results around the system, the use of a `record` type is _much_ better. -Now, the criteria query API offers a much more satisfying solution to the problem. +The criteria query API offers a much more satisfying solution to the problem. Consider the following code: [source,java] @@ -963,6 +988,18 @@ We have to make sure that the class with the `@NamedQuery` annotation will be sc - by adding `org.hibernate.example.BookQueries` to `persistence.xml`, or - by calling `configuration.addClass(BookQueries.class)`. +[TIP] +==== +Unfortunately, JPA's `@NamedQuery` annotation can't be placed on a package descriptor. +Therefore, Hibernate provides a very similar annotation, `@org.hibernate.annotations.NamedQuery` which _can_ be specified at the package level. +If we declare a named query at the package level, we must call: +[source,java] +---- +configuration.addPackage("org.hibernate.example") +---- +so that Hibernate knows where to find it. +==== + The `@NamedNativeQuery` annotation lets us do the same for native SQL queries. There's much less advantage to using `@NamedNativeQuery`, because there is very little that Hibernate can do to validate the correctness of a query written in the native SQL dialect of your database. @@ -989,6 +1026,17 @@ Here, `BookQueries_.QUERY_10_BOOKS_BY_TITLE` is a constant with value `"10BooksB Note that the code which executes the named query is not aware of whether the query was written in HQL or in native SQL, making it slightly easier to change and optimize the query later. +[TIP] +==== +:query-validator: https://github.com/hibernate/query-validator/ + +It's nice to have our queries checked at startup time. +It's even better to have them checked at compile time. +Back in <>, we mentioned that the {query-validator}[Query Validator] can do that for us. +In fact, the Query Validator will even check HQL query strings that occur as arguments to `createQuery()` and friends. +So if we use the Query Validator, there's not much advantage to the use of named queries. +==== + [[load-access]] === Controlling lookup by id @@ -1072,6 +1120,11 @@ The `Connection` passed to the work is the same connection being used by the ses If the work returns a value, use `doReturningWork()` instead of `doWork()`. +[TIP] +==== +In a container environment where transactions and database connections are managed by the container, this might not be the easiest way to obtain the JDBC connection. +==== + [[advice]] === What to do when things go wrong