HQL doc rewrite/restructure

- document new features of HQL (literals, functions, filter, rollup)
- rewrite parts of the section dealing with the Query API + execution
- split out a new chapter about the query language, and reorder sections
- remove material about deprecated/removed features
- get rid of use of java.sql.Timestamp from the code!
- make use of repeatable annotations in code examples
This commit is contained in:
Gavin King 2021-12-28 18:37:39 +01:00
parent 4a88399bb5
commit 3f8a0b6776
20 changed files with 2901 additions and 2504 deletions

View File

@ -22,7 +22,8 @@ include::chapters/fetching/Fetching.adoc[]
include::chapters/batch/Batching.adoc[]
include::chapters/caching/Caching.adoc[]
include::chapters/events/Events.adoc[]
include::chapters/query/hql/HQL.adoc[]
include::chapters/query/hql/Query.adoc[]
include::chapters/query/hql/QueryLanguage.adoc[]
include::chapters/query/criteria/Criteria.adoc[]
include::chapters/query/native/Native.adoc[]
include::chapters/query/spatial/Spatial.adoc[]

View File

@ -389,7 +389,7 @@ The {jpaJavadocUrlPrefix}NamedQueries.html[`@NamedQueries`] annotation is used t
The {jpaJavadocUrlPrefix}NamedQuery.html[`@NamedQuery`] annotation is used to specify a JPQL query that can be retrieved later by its name.
See the <<chapters/query/hql/HQL.adoc#jpql-api-named-query-example, `@NamedQuery`>> section for more info.
See the <<chapters/query/hql/Query.adoc#jpql-api-named-query-example, `@NamedQuery`>> section for more info.
[[annotations-jpa-namedstoredprocedurequeries]]
==== `@NamedStoredProcedureQueries`
@ -536,7 +536,7 @@ The {jpaJavadocUrlPrefix}PrimaryKeyJoinColumns.html[`@PrimaryKeyJoinColumns`] an
The {jpaJavadocUrlPrefix}QueryHint.html[`@QueryHint`] annotation is used to specify a Jakarta Persistence provider hint used by a `@NamedQuery` or a `@NamedNativeQuery` annotation.
See the <<chapters/query/hql/HQL.adoc#jpa-read-only-entities-native-example, `@QueryHint`>> section for more info.
See the <<chapters/query/hql/Query.adoc#jpa-read-only-entities-native-example, `@QueryHint`>> section for more info.
[[annotations-jpa-secondarytable]]
==== `@SecondaryTable`
@ -1088,7 +1088,7 @@ The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibern
- what SQL-level comment should be sent to the database
- if the query is read-only, hence it does not store the resulted entities into the currently running Persistence Context
See the <<chapters/query/hql/HQL.adoc#jpql-api-hibernate-named-query-example, `@NamedQuery`>> section for more info.
See the <<chapters/query/hql/Query.adoc#jpql-api-hibernate-named-query-example, `@NamedQuery`>> section for more info.
[[annotations-hibernate-nationalized]]
==== `@Nationalized`

View File

@ -293,7 +293,7 @@ include::{sourcedir}/BatchTest.java[tags=batch-bulk-hql-insert-example]
----
====
This section is only a brief overview of HQL. For more information, see <<chapters/query/hql/HQL.adoc#hql,HQL>>.
This section is only a brief overview of HQL. For more information, see <<chapters/query/hql/QueryLanguage.adoc#query-language,Hibernate Query Language>>.
[[batch-bulk-hql-strategies]]
==== Bulk-id strategies

View File

@ -0,0 +1,583 @@
[[hql]]
== HQL and JPQL
:modeldir: ../../../../../../main/java/org/hibernate/userguide/model
:sourcedir: ../../../../../../test/java/org/hibernate/userguide/hql
:extrasdir: extras
The Hibernate Query Language (HQL) and the Java Persistence Query Language (JPQL) are object-oriented query languages based on SQL and very similar in flavor to SQL.
[NOTE]
====
When we use the term "HQL" here, we usually mean both modern HQL, along with the standard subset defined by the specification.
====
HQL is not the only way to write queries in Hibernate:
- <<chapters/query/criteria/Criteria.adoc#criteria,criteria queries>> offer a Java-based API with greater compile-time typesafety, and
- <<chapters/query/native/Native.adoc#criteria,native SQL queries>> are also possible.
However, HQL is the most convenient option for most people most of the time.
The actual query language itself is discussed the <<chapters/query/criteria/QueryLanguage.adoc#query-language,next chapter>>.
This chapter describes the Java APIs for executing HQL and JPQL queries.
[[hql-getting-started]]
=== HQL query basics
A query may be provided to Hibernate as either:
* an _inline query_: the text of the query is passed as a string to the session at runtime, or
* a _named query_: the query is specified in an annotation or XML file, and identified by name at runtime.
The API for actually executing the query is the same in both cases.
[TIP]
====
One big advantage to named queries is that they are parsed by Hibernate at startup time, and so some sorts of errors are reported much earlier.
====
[[named-queries]]
==== Declaring named queries
Named queries may be defined using the Jakarta Persistence annotation `@NamedQuery`.
[[jpa-read-only-entities-native-example]]
.Declaring a named query with a query hint
====
[source, JAVA, indent=0]
----
include::{modeldir}/Person.java[tags=jpa-read-only-entities-native-example]
----
====
Alternatively, Hibernate offers an extended
https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/annotations/NamedQuery.html[`@NamedQuery`] annotation
which allows the specification of additional properties of the query, including flush mode, cacheability, and timeout interval, in a more typesafe way.
[[jpql-api-hibernate-named-query-example]]
.Declaring a named query using the typesafe annotation
====
[source, JAVA, indent=0]
----
include::{modeldir}/Phone.java[tags=jpql-api-hibernate-named-query-example, indent=0]
----
//include::{sourcedir}/HQLTest.java[tags=jpql-api-hibernate-named-query-example, indent=0]
====
[[hql-examples-domain-model]]
==== Example domain model
The code examples featured in this chapter, and the next, make use of the following annotated domain model.
[[hql-examples-domain-model-example]]
.Examples domain model
====
[source, JAVA, indent=0]
----
include::{modeldir}/Person.java[tags=hql-examples-domain-model-example]
include::{modeldir}/AddressType.java[tags=hql-examples-domain-model-example]
include::{modeldir}/Partner.java[tags=hql-examples-domain-model-example]
include::{modeldir}/Phone.java[tags=hql-examples-domain-model-example]
include::{modeldir}/PhoneType.java[tags=hql-examples-domain-model-example]
include::{modeldir}/Call.java[tags=hql-examples-domain-model-example]
include::{modeldir}/Payment.java[tags=hql-examples-domain-model-example]
include::{modeldir}/CreditCardPayment.java[tags=hql-examples-domain-model-example]
include::{modeldir}/WireTransferPayment.java[tags=hql-examples-domain-model-example]
----
====
[[query-api]]
==== Flavors of the Query API
To execute a query, you'll need an instance of the Jakarta Persistence `Query` interface, or, even better, of its subinterface `TypedQuery`.
[[jpa-query-api]]
===== Jakarta Persistence `Query` and `TypedQuery`
The `EntityManager` offers various operations that return `Query` or `TypedQuery<T>`, including:
- `EntityManager#createQuery()`, which accepts a query written in HQL, and
- `EntityManager#createNamedQuery()`, which accepts the name of a named query.
[TIP]
====
It's better to explicitly pass the query result type as a Java `Class<T>`.
That way, you'll obtain a `TypedQuery<T>`, and avoid some later typecasting.
====
[[jpql-api-example]]
.Obtaining a Jakarta Persistence `Query` or `TypedQuery` reference
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-example]
----
====
[[jpql-api-named-query-example]]
.Obtaining a Jakarta Persistence `Query` or `TypedQuery` reference for a named query
====
[source, JAVA, indent=0]
----
include::{modeldir}/Person.java[tags=jpql-api-named-query-example, indent=0]
include::{sourcedir}/HQLTest.java[tags=jpql-api-named-query-example, indent=0]
----
====
[[hql-query-api]]
===== Hibernate `Query`
Hibernate's `Session` interface refines the return types of the operations of `EntityManager` which create query objects.
`Session#createQuery()`, `Session#createNamedQuery()`, and other similar operations all return an instance of the extension `org.hibernate.query.Query`.
[IMPORTANT]
====
Some overloaded forms of these operations return a raw type, but in Hibernate 6 all of these have been deprecated, and the use of the raw type `Query` is now strongly discouraged.
Programs should migrate to the use of the typesafe overloads which accept a `Class<T>` object and return a typed `Query<T>`.
====
Hibernate's `Query` interface offers additional operations not available via `TypedQuery`, as we'll see below.
[[hql-api-example]]
.Obtaining a Hibernate `Query`
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-api-example]
----
====
[[hql-api-named-query-example]]
.Obtaining a Hibernate `Query` for a named query
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-api-named-query-example]
----
====
[[query-execution]]
=== Executing HQL and JPQL queries
Since `org.hibernate.query.Query` inherits `TypedQuery`, which in turn inherits `Query`, usage of the three interfaces is almost identical.
[[jpql-query-parameters]]
==== Binding arguments to query parameters
A query may have named parameters or positional parameters:
* named parameters are specified using the syntax `:name`, and
* positional parameters are specified using the syntax `?1`, `?2`, etc.
If the query has parameters, arguments must be bound to each parameter before the query is executed.
[[jpql-api-parameter-example]]
.Named parameter binding
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-parameter-example]
----
====
JPQL-style positional parameters are numbered from `1`.
Just like with named parameters, a positional parameter may appear multiple times in a query.
[[jpql-api-positional-parameter-example]]
.Positional parameter binding
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-positional-parameter-example]
----
====
[NOTE]
====
It's not a good idea to mix named and positional parameters in a single query.
====
[[jpql-query-execution]]
==== Executing the query
The `Query` interface is used to control the execution of the query.
* `Query#getResultList()` is useful when the query might return zero, or more than one result.
* `Query#getSingleResult()` is only for cases where the query always returns exactly one result.
It throws an exception when zero or many results are returned by the database.
* `Query#getResultStream()` allows results to be retrieved incrementally, using a database cursor.
[[jpql-api-list-example]]
.Executing a query using `getResultList()`
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-list-example]
----
====
[[jpql-api-unique-result-example]]
.Executing a query using `getSingleResult()`
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-single-result-example]
----
====
[[jpql-api-stream-example]]
.Executing a query using `getResultStream()`
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-stream-example]
----
====
[NOTE]
====
The `getResultStream()` method isn't usually useful.
It's almost always a bad idea to hold open a database cursor.
====
[[jpql-pagination]]
==== Pagination and limits
The very important methods `Query#setMaxResults()` and `Query#setFirstResult()` are used to limit the number of results and control pagination.
[[jpql-api-basic-usage-example]]
.Limits and pagination
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-basic-usage-example]
----
====
[[jpql-query-hints]]
==== Using query hints to control query execution
When working with the Jakarta Persistence API, advanced control over query execution is possible via named query hints.
For example, we may want to specify an execution timeout or control caching.
[[jpql-api-hint-usage-example]]
.`Query` execution using a query hint
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-hint-usage-example]
----
====
Jakarta Persistence defines some standard hints with the prefix `jakarta.persistence`, but most hints are provider specific.
Using provider-specific hints limits your program's portability to only a small degree.
|===
| `jakarta.persistence.query.timeout` | Defines the query timeout, in milliseconds.
| `jakarta.persistence.fetchgraph` | Defines a _fetchgraph_ EntityGraph.
Attributes explicitly specified as `AttributeNodes` are treated as `FetchType.EAGER` (via join fetch or subsequent select).
For details, see the EntityGraph discussions in <<chapters/fetching/Fetching.adoc#fetching,Fetching>>.
| `jakarta.persistence.loadgraph` | Defines a _loadgraph_ EntityGraph.
Attributes explicitly specified as AttributeNodes are treated as `FetchType.EAGER` (via join fetch or subsequent select).
Attributes that are not specified are treated as `FetchType.LAZY` or `FetchType.EAGER` depending on the attribute's definition in metadata.
For details, see the EntityGraph discussions in <<chapters/fetching/Fetching.adoc#fetching,Fetching>>.
| `org.hibernate.cacheMode` | Defines the `CacheMode` to use. See `org.hibernate.query.Query#setCacheMode`.
| `org.hibernate.cacheable` | Defines whether the query is cacheable. true/false. See `org.hibernate.query.Query#setCacheable`.
| `org.hibernate.cacheRegion` | For queries that are cacheable, defines a specific cache region to use. See `org.hibernate.query.Query#setCacheRegion`.
| `org.hibernate.comment` | Defines the comment to apply to the generated SQL. See `org.hibernate.query.Query#setComment`.
| `org.hibernate.fetchSize` | Defines the JDBC fetch-size to use. See `org.hibernate.query.Query#setFetchSize`.
| `org.hibernate.flushMode` | Defines the Hibernate-specific `FlushMode` to use. See `org.hibernate.query.Query#setFlushMode.` If possible, prefer using `jakarta.persistence.Query#setFlushMode` instead.
| `org.hibernate.readOnly` | Defines that entities and collections loaded by this query should be marked as read-only. See `org.hibernate.query.Query#setReadOnly`.
|===
For complete details, see the `Query` {jpaJavadocUrlPrefix}Query.html[Javadocs].
[TIP]
====
For named queries, query hints may be specified using the Jakarta Persistence {jpaJavadocUrlPrefix}QueryHint.html[`@QueryHint`] annotation.
====
[[hql-api-execution]]
==== Advanced control over query execution
When working directly with a Hibernate `Session`, the interface `org.hibernate.Query` is used to control the execution of the query.
Whereas we needed to specify some information using query hints when working with the Jakarta Persistence API, here we have typesafe setters:
|===
| `Query#setTimeout()` | Sets the JDBC-level query timeout.
| `Query#setFetchSize()` | Sets the JDBC-level fetch size.
| `Query#setCacheable()` and `setCacheRegion()` | Control query caching.
| `Query#setCacheMode()` | Overrides the session-level cache mode.
| `Query#setFlushMode()` | Overrides the session-level flush mode. Flushing is covered in detail in <<chapters/flushing/Flushing.adoc#flushing,Flushing>>.
| `Query#setLockMode()` | Overrides the session-level flush mode. Locking is covered in detail in <<chapters/locking/Locking.adoc#locking,Locking>>.
| `Query#setReadOnly()` | Overrides the session-level default for read-only state. The concept of read-only state is covered in <<chapters/pc/PersistenceContext.adoc#pc,Persistence Contexts>>.
| `Query#setComment()` | Adds a comment to the generated SQL.
|===
For complete details, see the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/Query.html[Query] Javadocs.
[[hql-api-basic-usage-example]]
.Advanced query control
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-api-basic-usage-example]
----
====
[IMPORTANT]
====
`addQueryHint()` allows specification of a database query hint.
A hint is added directly to the generated SQL according to `Dialect#getQueryHintString`.
On the other hand, `setHint()` refers to the Jakarta Persistence notion of a query hint, a hint that targets the provider (Hibernate).
This is a completely different concept.
====
[[hql-api-result-transformers]]
=== Query result transformers
A program may hook into the process of building the query results by providing a `org.hibernate.transform.ResultListTransformer` or `org.hibernate.transform.TupleTransformer`.
Hibernate provides several some built-in implementations of these interfaces, for example:
[[hql-api-result-transformers-example]]
.Using a `ResultListTransformer`
====
[source, JAVA, indent=0]
----
include::{sourcedir}/SelectDistinctTest.java[tags=hql-distinct-entity-resulttransformer-example]
----
====
See the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/transform/ResultListTransformer.html[Javadocs] along with the built-in implementations for additional details.
//[[hql-api-parameters]]
//==== Binding arguments to parameters
//
//The last thing that needs to happen before we can execute the query is to bind the values for any parameters defined in the query.
//`Query` defines many overloaded methods for this purpose.
//The most generic form takes the value as well as the Hibernate Type.
//
//[[hql-api-parameter-example]]
//.Hibernate name parameter binding
//====
//[source, JAVA, indent=0]
//----
//include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-example]
//----
//====
//
//Hibernate generally understands the expected type of the parameter given its context in the query.
//In the previous example since we are using the parameter in a `LIKE` comparison against a String-typed attribute Hibernate would automatically infer the type; so the above could be simplified.
//
//[[hql-api-parameter-inferred-type-example]]
//.Hibernate name parameter binding (inferred type)
//====
//[source, JAVA, indent=0]
//----
//include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-inferred-type-example]
//----
//====
//
//There are also short hand forms for binding common types such as strings, booleans, integers, etc.
//
//[[hql-api-parameter-short-form-example]]
//.Hibernate name parameter binding (short forms)
//====
//[source, JAVA, indent=0]
//----
//include::{sourcedir}/HQLTest.java[tags=hql-api-parameter-short-form-example]
//----
//====
//
//[IMPORTANT]
//====
//Traditionally, Hibernate used to support a JDBC positional parameter syntax form via a `?` symbol without a following ordinal.
//
//There was no way to relate two such positional parameters as being "the same" aside from binding the same value to each and, for this reason, this form is no longer supported.
//
//[source, JAVA, indent=0]
//----
//include::{sourcedir}/HQLTest.java[tags=hql-api-positional-parameter-example]
//----
//====
//
//In terms of execution, Hibernate offers 4 different methods. The 2 most commonly used are
//
//* `Query#list` - executes the select query and returns back the list of results.
//* `Query#uniqueResult` - executes the select query and returns the single result. If there were more than one result an exception is thrown.
//
//[[hql-api-list-example]]
//.Hibernate `list()` result
//====
//[source, JAVA, indent=0]
//----
//include::{sourcedir}/HQLTest.java[tags=hql-api-list-example]
//----
//====
//
//It is also possible to extract a single result from a `Query`.
//
//[[hql-api-unique-result-example]]
//.Hibernate `uniqueResult()`
//====
//[source, JAVA, indent=0]
//----
//include::{sourcedir}/HQLTest.java[tags=hql-api-unique-result-example]
//----
//====
//
//[NOTE]
//====
//If the unique result is used often and the attributes upon which it is based are unique, you may want to consider mapping a natural-id and using the natural-id loading API.
//See the <<chapters/domain/natural_id.adoc#naturalid,Natural Ids>> for more information on this topic.
//====
//
[[hql-read-only-entities]]
==== Querying for read-only entities
As explained in <<chapters/domain/immutability.adoc#entity-immutability,entity immutability>>, fetching entities in read-only mode is more efficient than fetching entities whose state changes might need to be written to the database.
Fortunately, even mutable entities may be fetched in read-only mode, with the benefit of reduced memory footprint and of a faster flushing process.
Read-only entities are skipped by the dirty checking mechanism as illustrated by the following example:
[[hql-read-only-entities-example]]
.Read-only entities query example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-read-only-entities-example]
----
[source, SQL, indent=0]
----
include::{extrasdir}/hql-read-only-entities-example.sql[]
----
====
In this example, no SQL `UPDATE` was executed.
The method `Query#setReadOnly()` is an alternative to using a Jakarta Persistence query hint:
[[hql-read-only-entities-native-example]]
.Read-only entities native query example
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-read-only-entities-native-example]
----
====
[[hql-api-incremental]]
=== Scrolling and streaming results
The `org.hibernate.Query` interface offers two specialized operations for reading query results incrementally, while maintaining an open JDBC `ResultSet` mapped to a server-side cursor.
[[hql-api-scroll]]
==== Scrollable result sets
`Query#scroll()` returns a `org.hibernate.ScrollableResults` which wraps an underlying JDBC scrollable `ResultSet`.
Depending on the specified `ScrollMode`, and on the capabilities of the JDBC driver, the `ScrollableResults` may allow navigation of the `ResultSet` in either direction.
[[hql-api-scroll-example]]
.Scrolling through a `ResultSet` containing entities
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-api-scroll-example]
----
====
If a `ScrollableResults` is left unclosed by the application, Hibernate will automatically close the underlying resources when the transaction ends.
However, it's much better to close the `ResultSet` as soon as possible.
[IMPORTANT]
====
Since this method holds the JDBC `ResultSet` open, the program should always close a `ScrollableResults` either explicitly, by calling `close()`, or using a https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html[try-with-resources] block.
====
[NOTE]
====
If you plan to use `Query#scroll` with collection fetching, it's important that your query explicitly order the results so that the JDBC results contain the related rows sequentially.
====
[[jpql-api-stream]]
==== Streamed result sets
Similarly, `getResultStream()` is a specialized operation for reading query results incrementally, while maintaining an open JDBC `ResultSet` mapped to a server-side cursor.
[IMPORTANT]
====
The `getResultStream()` method is not just a convenient way to obtain a Java `Stream`.
For that, use `getResultList().stream()`.
====
[[hql-api-stream-projection-example]]
.Hibernate `getResultStream()` with a projection result type
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-api-stream-projection-example]
----
====
[[hql-api-stream-example]]
.Hibernate `getResultStream()` with an entity result type
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-api-stream-example]
----
====
Hibernate will automatically close the underlying resources (the JDBC `ResultSet`) when the transaction ends.
However, it's much better to close the `ResultSet` as soon as possible.
[IMPORTANT]
====
The program should always close a `Stream` either explicitly, by calling `close()`, or using a https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html[try-with-resources] block.
====
There's one exception to this rule: the `Stream` is automatically closed after a terminal operation, as illustrated by the following example:
[[jpql-api-stream-terminal-operation]]
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=jpql-api-stream-terminal-operation]
----
====
Here, the `Stream` is closed automatically after the `collect()` method is called.
[[hql-query-plan-cache]]
=== Entity query plan cache
Any entity query, be it JPQL or Criteria API, has to be parsed into an AST (Abstract Syntax Tree) so that Hibernate can generate the proper SQL statement. The entity query compilation takes time, and for this reason, Hibernate offers a query plan cache.
When executing an entity query, Hibernate first checks the plan cache, and only if there's no plan available, a new one will be computed right away.
The query plan cache can be configured via the following configuration properties:
`hibernate.query.plan_cache_max_size`::
This setting gives the maximum number of entries of the plan cache. The default value is 2048.
`hibernate.query.plan_parameter_metadata_max_size`::
The setting gives the maximum number of `ParameterMetadataImpl` instances maintained by the query plan cache. The `ParameterMetadataImpl` object encapsulates metadata about parameters encountered within a query. The default value is 128.
Now, if you have many JPQL or Criteria API queries, it's a good idea to increase the query plan cache size so that the vast majority of executing entity queries can skip the compilation phase, therefore reducing execution time.
To get a better understanding of the query plan cache effectiveness, Hibernate offers several statistics you can use. For more details, check out the <<chapters/statistics/Statistics.adoc#statistics-query-plan-cache,Query plan cache statistics>> section.

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,12 @@
SELECT DISTINCT
p.id as id1_1_0_,
b.id as id1_0_1_,
p.first_name as first_na2_1_0_,
p.last_name as last_nam3_1_0_,
b.author_id as author_i3_0_1_,
b.title as title2_0_1_,
b.author_id as author_i3_0_0__,
b.id as id1_0_0__
FROM person p
LEFT OUTER JOIN book b ON p.id=b.author_id
select distinct
p.id,
b.author_id,
b.id,
b.title,
p.first_name,
p.last_name
from
person p
left join
book b
on p.id=b.author_id

View File

@ -1,11 +1,12 @@
SELECT
p.id as id1_1_0_,
b.id as id1_0_1_,
p.first_name as first_na2_1_0_,
p.last_name as last_nam3_1_0_,
b.author_id as author_i3_0_1_,
b.title as title2_0_1_,
b.author_id as author_i3_0_0__,
b.id as id1_0_0__
FROM person p
LEFT OUTER JOIN book b ON p.id=b.author_id
select
p.id,
b.author_id,
b.id,
b.title,
p.first_name,
p.last_name
from
person p
left join
book b
on p.id=b.author_id

View File

@ -1,3 +1,4 @@
SELECT DISTINCT
p.last_name as col_0_0_
FROM person p
select distinct
p.last_name
from
person p

View File

@ -1,7 +1,12 @@
SELECT c.id AS id1_5_ ,
c.duration AS duration2_5_ ,
c.phone_id AS phone_id4_5_ ,
c.call_timestamp AS call_tim3_5_
FROM phone_call c
INNER JOIN phone p ON c.phone_id = p.id
WHERE p.phone_number = '123-456-7890'
select
c.id,
c.duration,
c.phone_id,
c.call_timestamp
from
phone_call c
join
Phone p
on p.id=c.phone_id
where
p.phone_number='123-456-7890'

View File

@ -1,6 +1,6 @@
select_statement :: =
[select_clause]
from_clause
[from_clause]
[where_clause]
[groupby_clause]
[having_clause]

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.userguide.model;
import java.time.LocalDateTime;
import java.util.Date;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@ -30,7 +31,7 @@ public class Call {
private Phone phone;
@Column(name = "call_timestamp")
private Date timestamp;
private LocalDateTime timestamp;
private int duration;
@ -55,11 +56,11 @@ public class Call {
this.phone = phone;
}
public Date getTimestamp() {
public LocalDateTime getTimestamp() {
return timestamp;
}
public void setTimestamp(Date timestamp) {
public void setTimestamp(LocalDateTime timestamp) {
this.timestamp = timestamp;
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.userguide.model;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@ -42,155 +43,146 @@ import jakarta.persistence.Version;
/**
* @author Vlad Mihalcea
*/
@NamedNativeQueries({
//tag::sql-scalar-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_name",
query =
"SELECT name " +
"FROM Person "
),
//end::sql-scalar-NamedNativeQuery-example[]
//tag::sql-multiple-scalar-values-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_name_and_nickName",
query =
"SELECT " +
" name, " +
" nickName " +
"FROM Person "
),
//end::sql-multiple-scalar-values-NamedNativeQuery-example[]
// tag::sql-multiple-scalar-values-dto-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_name_and_nickName_dto",
query =
"SELECT " +
" name, " +
" nickName " +
"FROM Person ",
resultSetMapping = "name_and_nickName_dto"
),
//end::sql-multiple-scalar-values-dto-NamedNativeQuery-example[]
//tag::sql-entity-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_by_name",
query =
"SELECT " +
" p.id AS \"id\", " +
" p.name AS \"name\", " +
" p.nickName AS \"nickName\", " +
" p.address AS \"address\", " +
" p.createdOn AS \"createdOn\", " +
" p.version AS \"version\" " +
"FROM Person p " +
"WHERE p.name LIKE :name",
resultClass = Person.class
),
//end::sql-entity-NamedNativeQuery-example[]
//tag::sql-entity-associations-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_with_phones_by_name",
query =
"SELECT " +
" pr.id AS \"pr.id\", " +
" pr.name AS \"pr.name\", " +
" pr.nickName AS \"pr.nickName\", " +
" pr.address AS \"pr.address\", " +
" pr.createdOn AS \"pr.createdOn\", " +
" pr.version AS \"pr.version\", " +
" ph.id AS \"ph.id\", " +
" ph.person_id AS \"ph.person_id\", " +
" ph.phone_number AS \"ph.number\", " +
" ph.phone_type AS \"ph.type\" " +
"FROM Person pr " +
"JOIN Phone ph ON pr.id = ph.person_id " +
"WHERE pr.name LIKE :name",
resultSetMapping = "person_with_phones"
//tag::sql-scalar-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_name",
query =
"SELECT name " +
"FROM Person "
)
//end::sql-scalar-NamedNativeQuery-example[]
//tag::sql-multiple-scalar-values-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_name_and_nickName",
query =
"SELECT " +
" name, " +
" nickName " +
"FROM Person "
)
//end::sql-multiple-scalar-values-NamedNativeQuery-example[]
// tag::sql-multiple-scalar-values-dto-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_name_and_nickName_dto",
query =
"select " +
" name, " +
" nickName " +
"from Person ",
resultSetMapping = "name_and_nickName_dto"
)
//end::sql-multiple-scalar-values-dto-NamedNativeQuery-example[]
//tag::sql-entity-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_by_name",
query =
"select " +
" p.id AS \"id\", " +
" p.name AS \"name\", " +
" p.nickName AS \"nickName\", " +
" p.address AS \"address\", " +
" p.createdOn AS \"createdOn\", " +
" p.version AS \"version\" " +
"from Person p " +
"where p.name LIKE :name",
resultClass = Person.class
)
//end::sql-entity-NamedNativeQuery-example[]
//tag::sql-entity-associations-NamedNativeQuery-example[]
@NamedNativeQuery(
name = "find_person_with_phones_by_name",
query =
"select " +
" pr.id AS \"pr.id\", " +
" pr.name AS \"pr.name\", " +
" pr.nickName AS \"pr.nickName\", " +
" pr.address AS \"pr.address\", " +
" pr.createdOn AS \"pr.createdOn\", " +
" pr.version AS \"pr.version\", " +
" ph.id AS \"ph.id\", " +
" ph.person_id AS \"ph.person_id\", " +
" ph.phone_number AS \"ph.number\", " +
" ph.phone_type AS \"ph.type\" " +
"from Person pr " +
"join Phone ph ON pr.id = ph.person_id " +
"where pr.name LIKE :name",
resultSetMapping = "person_with_phones"
)
//end::sql-entity-associations-NamedNativeQuery-example[]
//tag::sql-entity-associations-NamedNativeQuery-example[]
@SqlResultSetMapping(
name = "person_with_phones",
entities = {
@EntityResult(
entityClass = Person.class,
fields = {
@FieldResult( name = "id", column = "pr.id" ),
@FieldResult( name = "name", column = "pr.name" ),
@FieldResult( name = "nickName", column = "pr.nickName" ),
@FieldResult( name = "address", column = "pr.address" ),
@FieldResult( name = "createdOn", column = "pr.createdOn" ),
@FieldResult( name = "version", column = "pr.version" ),
}
),
@EntityResult(
entityClass = Phone.class,
fields = {
@FieldResult( name = "id", column = "ph.id" ),
@FieldResult( name = "person", column = "ph.person_id" ),
@FieldResult( name = "number", column = "ph.number" ),
@FieldResult( name = "type", column = "ph.type" ),
}
)
}
)
//end::sql-entity-associations-NamedNativeQuery-example[]
//tag::sql-multiple-scalar-values-dto-NamedNativeQuery-example[]
@SqlResultSetMapping(
name = "name_and_nickName_dto",
classes = @ConstructorResult(
targetClass = PersonNames.class,
columns = {
@ColumnResult(name = "name"),
@ColumnResult(name = "nickName")
}
)
//end::sql-entity-associations-NamedNativeQuery-example[]
})
@SqlResultSetMappings({
//tag::sql-entity-associations-NamedNativeQuery-example[]
@SqlResultSetMapping(
name = "person_with_phones",
entities = {
@EntityResult(
entityClass = Person.class,
fields = {
@FieldResult( name = "id", column = "pr.id" ),
@FieldResult( name = "name", column = "pr.name" ),
@FieldResult( name = "nickName", column = "pr.nickName" ),
@FieldResult( name = "address", column = "pr.address" ),
@FieldResult( name = "createdOn", column = "pr.createdOn" ),
@FieldResult( name = "version", column = "pr.version" ),
}
),
@EntityResult(
entityClass = Phone.class,
fields = {
@FieldResult( name = "id", column = "ph.id" ),
@FieldResult( name = "person", column = "ph.person_id" ),
@FieldResult( name = "number", column = "ph.number" ),
@FieldResult( name = "type", column = "ph.type" ),
}
)
}
),
//end::sql-entity-associations-NamedNativeQuery-example[]
//tag::sql-multiple-scalar-values-dto-NamedNativeQuery-example[]
@SqlResultSetMapping(
name = "name_and_nickName_dto",
classes = @ConstructorResult(
targetClass = PersonNames.class,
columns = {
@ColumnResult(name = "name"),
@ColumnResult(name = "nickName")
}
)
)
//end::sql-multiple-scalar-values-dto-NamedNativeQuery-example[]
})
)
//end::sql-multiple-scalar-values-dto-NamedNativeQuery-example[]
//tag::hql-examples-domain-model-example[]
@NamedQueries({
//tag::jpql-api-named-query-example[]
@NamedQuery(
name = "get_person_by_name",
query = "select p from Person p where name = :name"
)
//end::jpql-api-named-query-example[]
,
// tag::jpa-read-only-entities-native-example[]
@NamedQuery(
name = "get_read_only_person_by_name",
query = "select p from Person p where name = :name",
hints = {
@QueryHint(
name = "org.hibernate.readOnly",
value = "true"
)
}
)
//end::jpa-read-only-entities-native-example[]
})
//tag::jpql-api-named-query-example[]
@NamedQuery(
name = "get_person_by_name",
query = "select p from Person p where name = :name"
)
//end::jpql-api-named-query-example[]
// tag::jpa-read-only-entities-native-example[]
@NamedQuery(
name = "get_read_only_person_by_name",
query = "select p from Person p where name = :name",
hints = {
@QueryHint(
name = "org.hibernate.readOnly",
value = "true"
)
}
)
//end::jpa-read-only-entities-native-example[]
//tag::sql-sp-ref-cursor-oracle-named-query-example[]
@NamedStoredProcedureQueries(
@NamedStoredProcedureQuery(
name = "sp_person_phones",
procedureName = "sp_person_phones",
parameters = {
@StoredProcedureParameter(
name = "personId",
type = Long.class,
mode = ParameterMode.IN
),
@StoredProcedureParameter(
name = "personPhones",
type = Class.class,
mode = ParameterMode.REF_CURSOR
)
}
)
@NamedStoredProcedureQuery(
name = "sp_person_phones",
procedureName = "sp_person_phones",
parameters = {
@StoredProcedureParameter(
name = "personId",
type = Long.class,
mode = ParameterMode.IN
),
@StoredProcedureParameter(
name = "personPhones",
type = Class.class,
mode = ParameterMode.REF_CURSOR
)
}
)
//end::sql-sp-ref-cursor-oracle-named-query-example[]
@Entity
@ -206,8 +198,7 @@ public class Person {
private String address;
@Temporal(TemporalType.TIMESTAMP )
private Date createdOn;
private LocalDateTime createdOn;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
@OrderColumn(name = "order_id")
@ -254,11 +245,11 @@ public class Person {
this.address = address;
}
public Date getCreatedOn() {
public LocalDateTime getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
public void setCreatedOn(LocalDateTime createdOn) {
this.createdOn = createdOn;
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.userguide.model;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
@ -17,18 +18,14 @@ import jakarta.persistence.ColumnResult;
import jakarta.persistence.ConstructorResult;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityResult;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.FieldResult;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapKey;
import jakarta.persistence.MapKeyTemporal;
import jakarta.persistence.OneToMany;
import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.TemporalType;
import org.hibernate.annotations.NamedNativeQueries;
import org.hibernate.annotations.NamedNativeQuery;
@ -39,30 +36,26 @@ import org.hibernate.annotations.NamedQuery;
* @author Vlad Mihalcea
*/
//tag::jpql-api-hibernate-named-query-example[]
@NamedQueries({
@NamedQuery(
name = "get_phone_by_number",
query = "select p " +
"from Phone p " +
"where p.number = :number",
timeout = 1,
readOnly = true
)
})
@NamedQuery(
name = "get_phone_by_number",
query = "select p " +
"from Phone p " +
"where p.number = :number",
timeout = 1,
readOnly = true
)
//end::jpql-api-hibernate-named-query-example[]
//tag::sql-multiple-scalar-values-dto-NamedNativeQuery-hibernate-example[]
@NamedNativeQueries({
@NamedNativeQuery(
name = "get_person_phone_count",
query = "SELECT pr.name AS name, count(*) AS phoneCount " +
"FROM Phone p " +
"JOIN Person pr ON pr.id = p.person_id " +
"GROUP BY pr.name",
resultSetMapping = "person_phone_count",
timeout = 1,
readOnly = true
),
})
@NamedNativeQuery(
name = "get_person_phone_count",
query = "select pr.name AS name, count(*) AS phoneCount " +
"from Phone p " +
"join Person pr ON pr.id = p.person_id " +
"group BY pr.name",
resultSetMapping = "person_phone_count",
timeout = 1,
readOnly = true
)
@SqlResultSetMapping(
name = "person_phone_count",
classes = @ConstructorResult(
@ -97,12 +90,11 @@ public class Phone {
//tag::hql-collection-qualification-example[]
@OneToMany(mappedBy = "phone")
@MapKey(name = "timestamp")
@MapKeyTemporal(TemporalType.TIMESTAMP )
private Map<Date, Call> callHistory = new HashMap<>();
private Map<LocalDateTime, Call> callHistory = new HashMap<>();
//end::hql-collection-qualification-example[]
@ElementCollection
private List<Date> repairTimestamps = new ArrayList<>( );
private List<LocalDateTime> repairTimestamps = new ArrayList<>( );
//Getters and setters are omitted for brevity
@ -145,11 +137,11 @@ public class Phone {
return calls;
}
public Map<Date, Call> getCallHistory() {
public Map<LocalDateTime, Call> getCallHistory() {
return callHistory;
}
public List<Date> getRepairTimestamps() {
public List<LocalDateTime> getRepairTimestamps() {
return repairTimestamps;
}

View File

@ -7,9 +7,7 @@
package org.hibernate.userguide.criteria;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
@ -67,14 +65,14 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase {
Person person1 = new Person("John Doe" );
person1.setNickName( "JD" );
person1.setAddress( "Earth" );
person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ;
person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ;
person1.getAddresses().put( AddressType.HOME, "Home address" );
person1.getAddresses().put( AddressType.OFFICE, "Office address" );
entityManager.persist(person1);
Person person2 = new Person("Mrs. John Doe" );
person2.setAddress( "Earth" );
person2.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ;
person2.setCreatedOn( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ) ) ;
entityManager.persist(person2);
Person person3 = new Person("Dr_ John Doe" );
@ -84,16 +82,16 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase {
phone1.setId( 1L );
phone1.setType( PhoneType.MOBILE );
person1.addPhone( phone1 );
phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
phone1.getRepairTimestamps().add( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ) );
phone1.getRepairTimestamps().add( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ) );
Call call11 = new Call();
call11.setDuration( 12 );
call11.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
call11.setTimestamp( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) );
Call call12 = new Call();
call12.setDuration( 33 );
call12.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
call12.setTimestamp( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ) );
phone1.addCall(call11);
phone1.addCall(call12);

View File

@ -22,11 +22,12 @@ import jakarta.persistence.Table;
import org.hibernate.jpa.QueryHints;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.transform.DistinctResultTransformer;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
@ -65,9 +66,9 @@ public class SelectDistinctTest extends BaseEntityManagerFunctionalTestCase {
@Test
public void testDistinctProjection() {
doInJPA( this::entityManagerFactory, entityManager -> {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::hql-distinct-projection-query-example[]
List<String> lastNames = entityManager.createQuery(
List<String> lastNames = entityManager.createQuery(
"select distinct p.lastName " +
"from Person p", String.class)
.getResultList();
@ -143,6 +144,28 @@ public class SelectDistinctTest extends BaseEntityManagerFunctionalTestCase {
});
}
@Test
public void testDistinctAuthorsWithResultTransformer() {
doInHibernate( this::entityManagerFactory, session -> {
//tag::hql-distinct-entity-resulttransformer-example[]
List<Person> authors = session.createQuery(
"select p " +
"from Person p " +
"left join fetch p.books", Person.class)
.setResultListTransformer( DistinctResultTransformer.INSTANCE )
.getResultList();
//end::hql-distinct-entity-resulttransformer-example[]
authors.forEach( author -> {
log.infof( "Author %s wrote %d books",
author.getFirstName() + " " + author.getLastName(),
author.getBooks().size()
);
} );
});
}
@Entity(name = "Person") @Table( name = "person")
public static class Person {

View File

@ -3,10 +3,8 @@ package org.hibernate.userguide.sql;
import java.sql.CallableStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Pattern;
@ -104,7 +102,7 @@ public class MySQLStoredProcedureTest extends BaseEntityManagerFunctionalTestCas
Person person1 = new Person("John Doe" );
person1.setNickName( "JD" );
person1.setAddress( "Earth" );
person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ;
person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ;
person1.getAddresses().put( AddressType.HOME, "Home address" );
person1.getAddresses().put( AddressType.OFFICE, "Office address" );

View File

@ -100,7 +100,7 @@ public class OracleStoredProcedureTest extends BaseEntityManagerFunctionalTestCa
Person person1 = new Person("John Doe" );
person1.setNickName( "JD" );
person1.setAddress( "Earth" );
person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ;
person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ;
person1.getAddresses().put( AddressType.HOME, "Home address" );
person1.getAddresses().put( AddressType.OFFICE, "Office address" );

View File

@ -7,9 +7,7 @@
package org.hibernate.userguide.sql;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.List;
import jakarta.persistence.PersistenceException;
@ -70,14 +68,14 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
Person person1 = new Person("John Doe" );
person1.setNickName( "JD" );
person1.setAddress( "Earth" );
person1.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ;
person1.setCreatedOn( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) ) ;
person1.getAddresses().put( AddressType.HOME, "Home address" );
person1.getAddresses().put( AddressType.OFFICE, "Office address" );
entityManager.persist(person1);
Person person2 = new Person("Mrs. John Doe" );
person2.setAddress( "Earth" );
person2.setCreatedOn( Timestamp.from( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) )) ;
person2.setCreatedOn( LocalDateTime.of( 2000, 1, 2, 12, 0, 0 ) ) ;
entityManager.persist(person2);
Person person3 = new Person("Dr_ John Doe" );
@ -87,16 +85,16 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
phone1.setId( 1L );
phone1.setType( PhoneType.MOBILE );
person1.addPhone( phone1 );
phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
phone1.getRepairTimestamps().add( Timestamp.from( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
phone1.getRepairTimestamps().add( LocalDateTime.of( 2005, 1, 1, 12, 0, 0 ) );
phone1.getRepairTimestamps().add( LocalDateTime.of( 2006, 1, 1, 12, 0, 0 ) );
Call call11 = new Call();
call11.setDuration( 12 );
call11.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
call11.setTimestamp( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ) );
Call call12 = new Call();
call12.setDuration( 33 );
call12.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ).toInstant( ZoneOffset.UTC ) ) );
call12.setTimestamp( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ) );
phone1.addCall(call11);
phone1.addCall(call12);