From 2167b442a420260393060f5d29c3894814ba81d8 Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Wed, 10 Feb 2016 18:07:48 +0200 Subject: [PATCH] Migrate User Guide Native chapter extras to test folder up to stored procedures --- .../userguide/Hibernate_User_Guide.adoc | 1 + .../appendices/Legacy_Native_Queries.adoc | 375 +++++++ .../chapters/query-native/Native.adoc | 932 +++++++++--------- ...iations-query-many-to-one-join-example.sql | 5 + ...iations-query-one-to-many-join-example.sql | 3 + ...iations-query-many-to-one-join-example.sql | 5 + ...iations-query-one-to-many-join-example.sql | 3 + .../userguide/model/AddressType.java | 6 + .../org/hibernate/userguide/model/Call.java | 8 +- .../userguide/model/CreditCardPayment.java | 6 + .../hibernate/userguide/model/Partner.java | 10 +- .../hibernate/userguide/model/Payment.java | 9 +- .../org/hibernate/userguide/model/Person.java | 123 +++ .../userguide/model/PersonNames.java | 32 + .../org/hibernate/userguide/model/Phone.java | 9 +- .../hibernate/userguide/model/PhoneType.java | 6 + .../userguide/model/WireTransferPayment.java | 6 + .../userguide/criteria/PersonWrapper.java | 6 + .../userguide/hql/CallStatistics.java | 6 + .../locking/ExplicitLockingTest.java | 6 + .../locking/OptimisticLockingTest.java | 6 + .../org/hibernate/userguide/sql/Captain.java | 32 + .../hibernate/userguide/sql/Dimensions.java | 40 + .../org/hibernate/userguide/sql/Identity.java | 60 ++ .../userguide/sql/PersonSummaryDTO.java | 37 + .../org/hibernate/userguide/sql/SQLTest.java | 761 ++++++++++++++ .../hibernate/userguide/sql/SpaceShip.java | 124 +++ .../transactions/TransactionsTest.java | 6 + .../src/test/resources/log4j.properties | 3 +- 29 files changed, 2127 insertions(+), 499 deletions(-) create mode 100644 documentation/src/main/asciidoc/userguide/appendices/Legacy_Native_Queries.adoc create mode 100644 documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-hibernate-entity-associations-query-many-to-one-join-example.sql create mode 100644 documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-hibernate-entity-associations-query-one-to-many-join-example.sql create mode 100644 documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-jpa-entity-associations-query-many-to-one-join-example.sql create mode 100644 documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-jpa-entity-associations-query-one-to-many-join-example.sql create mode 100644 documentation/src/main/java/org/hibernate/userguide/model/PersonNames.java create mode 100644 documentation/src/test/java/org/hibernate/userguide/sql/Captain.java create mode 100644 documentation/src/test/java/org/hibernate/userguide/sql/Dimensions.java create mode 100644 documentation/src/test/java/org/hibernate/userguide/sql/Identity.java create mode 100644 documentation/src/test/java/org/hibernate/userguide/sql/PersonSummaryDTO.java create mode 100644 documentation/src/test/java/org/hibernate/userguide/sql/SQLTest.java create mode 100644 documentation/src/test/java/org/hibernate/userguide/sql/SpaceShip.java diff --git a/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc b/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc index ea2f65199e..3e5c921a01 100644 --- a/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc +++ b/documentation/src/main/asciidoc/userguide/Hibernate_User_Guide.adoc @@ -30,6 +30,7 @@ include::chapters/portability/Portability.adoc[] include::appendices/Legacy_Bootstrap.adoc[] include::appendices/Legacy_DomainModel.adoc[] include::appendices/Legacy_Criteria.adoc[] +include::appendices/Legacy_Native_Queries.adoc[] include::Bibliography.adoc[] diff --git a/documentation/src/main/asciidoc/userguide/appendices/Legacy_Native_Queries.adoc b/documentation/src/main/asciidoc/userguide/appendices/Legacy_Native_Queries.adoc new file mode 100644 index 0000000000..d4764de6c2 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/appendices/Legacy_Native_Queries.adoc @@ -0,0 +1,375 @@ +[[appendix-legacy-native-queries]] +== Legacy Hibernate Native Queries + +[[legacy-sql-named-queries]] +=== Legacy Named SQL queries + +Named SQL queries can also be defined during mapping and called in exactly the same way as a named HQL query. +In this case, you do _not_ need to call `addEntity()` anymore. + +.Named sql query using the `` mapping element +==== +[source,xml] +---- + + + SELECT person.NAME AS {person.name}, + person.AGE AS {person.age}, + person.SEX AS {person.sex} + FROM PERSON person + WHERE person.NAME LIKE :namePattern + +---- +==== + +.Execution of a named query +==== +[source, JAVA, indent=0] +---- +List people = session + .getNamedQuery( "persons" ) + .setString( "namePattern", namePattern ) + .setMaxResults( 50 ) + .list(); +---- +==== + +The `` element is use to join associations and the `` element is used to define queries which initialize collections. + +.Named sql query with association +==== +[source,xml] +---- + + + + SELECT person.NAME AS {person.name}, + person.AGE AS {person.age}, + person.SEX AS {person.sex}, + address.STREET AS {address.street}, + address.CITY AS {address.city}, + address.STATE AS {address.state}, + address.ZIP AS {address.zip} + FROM PERSON person + JOIN ADDRESS address + ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' + WHERE person.NAME LIKE :namePattern + +---- +==== + +A named SQL query may return a scalar value. +You must declare the column alias and Hibernate type using the `` element: + +.Named query returning a scalar +==== +[source,xml] +---- + + + + SELECT p.NAME AS name, + p.AGE AS age, + FROM PERSON p WHERE p.NAME LIKE 'Hiber%' + +---- +==== + +You can externalize the resultset mapping information in a `` element which will allow you to either reuse them across several named queries or through the `setResultSetMapping()` API. + +. mapping used to externalize mappinginformation +==== +[source,xml] +---- + + + + + + + SELECT person.NAME AS {person.name}, + person.AGE AS {person.age}, + person.SEX AS {person.sex}, + address.STREET AS {address.street}, + address.CITY AS {address.city}, + address.STATE AS {address.state}, + address.ZIP AS {address.zip} + FROM PERSON person + JOIN ADDRESS address + ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' + WHERE person.NAME LIKE :namePattern + +---- +==== + +You can, alternatively, use the resultset mapping information in your hbm files directly in java code. + +.Programmatically specifying the result mapping information +==== +[source, JAVA, indent=0] +---- +List cats = session + .createSQLQuery( "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id" ) + .setResultSetMapping("catAndKitten") + .list(); +---- +==== + +[[legacy-propertyresults]] +=== Legacy return-property to explicitly specify column/alias names + +You can explicitly tell Hibernate what column aliases to use with ``, instead of using the `{}` syntax to let Hibernate inject its own aliases. +For example: + +[source,xml] +---- + + + + + + + SELECT person.NAME AS myName, + person.AGE AS myAge, + person.SEX AS mySex, + FROM PERSON person WHERE person.NAME LIKE :name + +---- + +`` also works with multiple columns. +This solves a limitation with the `{}` syntax which cannot allow fine grained control of multi-column properties. + +[source,xml] +---- + + + + + + + + + SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer}, + STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate}, + REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY + FROM EMPLOYMENT + WHERE EMPLOYER = :id AND ENDDATE IS NULL + ORDER BY STARTDATE ASC + +---- + +In this example `` was used in combination with the `{}` syntax for injection. +This allows users to choose how they want to refer column and properties. + +If your mapping has a discriminator you must use `` to specify the discriminator column. + +[[legacy-sp_query]] +=== Legacy stored procedures for querying + +Hibernate provides support for queries via stored procedures and functions. +Most of the following documentation is equivalent for both. +The stored procedure/function must return a resultset as the first out-parameter to be able to work with Hibernate. +An example of such a stored function in Oracle 9 and higher is as follows: + +[source,xml] +---- +CREATE OR REPLACE FUNCTION selectAllEmployments + RETURN SYS_REFCURSOR +AS + st_cursor SYS_REFCURSOR; +BEGIN + OPEN st_cursor FOR + SELECT EMPLOYEE, EMPLOYER, + STARTDATE, ENDDATE, + REGIONCODE, EID, VALUE, CURRENCY + FROM EMPLOYMENT; + RETURN st_cursor; +END; +---- + +To use this query in Hibernate you need to map it via a named query. + +[source,xml] +---- + + + + + + + + + + + + + + { ? = call selectAllEmployments() } + +---- + +Stored procedures currently only return scalars and entities. +`` and `` are not supported. + +[[legacy-sql-limits-storedprocedures]] +=== Legacy rules/limitations for using stored procedures + +You cannot use stored procedures with Hibernate unless you follow some procedure/function rules. +If they do not follow those rules they are not usable with Hibernate. +If you still want to use these procedures you have to execute them via `session.doWork()`. + +The rules are different for each database, since database vendors have different stored procedure semantics/syntax. + +Stored procedure queries cannot be paged with `setFirstResult()/setMaxResults()`. + +The recommended call form is standard SQL92: `{ ? = call functionName() }` or `{ ? = call procedureName(}`. +Native call syntax is not supported. + +For Oracle the following rules apply: + +* A function must return a result set. +The first parameter of a procedure must be an `OUT` that returns a result set. +This is done by using a `SYS_REFCURSOR` type in Oracle 9 or 10. +In Oracle you need to define a `REF CURSOR` type. +See Oracle literature for further information. + +For Sybase or MS SQL server the following rules apply: + +* The procedure must return a result set. +Note that since these servers can return multiple result sets and update counts, Hibernate will iterate the results and take the first result that is a result set as its return value. +Everything else will be discarded. +* If you can enable `SET NOCOUNT ON` in your procedure it will probably be more efficient, but this is not a requirement. + +[[legacy-sql-cud]] +=== Legacy custom SQL for create, update and delete + +Hibernate can use custom SQL for create, update, and delete operations. +The SQL can be overridden at the statement level or individual column level. +This section describes statement overrides. +For columns, see <>. +The following example shows how to define custom SQL operations using annotations. + +.Custom CRUD XML +==== +[source,xml] +---- + + + + + + INSERT INTO PERSON (NAME, ID) VALUES ( UPPER(?), ? ) + UPDATE PERSON SET NAME=UPPER(?) WHERE ID=? + DELETE FROM PERSON WHERE ID=? + +---- +==== + +[NOTE] +==== +If you expect to call a store procedure, be sure to set the `callable` attribute to `true`, in annotations as well as in xml. +==== + +To check that the execution happens correctly, Hibernate allows you to define one of those three strategies: + +* none: no check is performed: the store procedure is expected to fail upon issues +* count: use of rowcount to check that the update is successful +* param: like COUNT but using an output parameter rather that the standard mechanism + +To define the result check style, use the `check` parameter which is again available in annotations as well as in xml. + +Last but not least, stored procedures are in most cases required to return the number of rows inserted, updated and deleted. +Hibernate always registers the first statement parameter as a numeric output parameter for the CUD operations: + +.Stored procedures and their return value +==== +[source] +---- +CREATE OR REPLACE FUNCTION updatePerson (uid IN NUMBER, uname IN VARCHAR2) + RETURN NUMBER IS +BEGIN + + update PERSON + set + NAME = uname, + where + ID = uid; + + return SQL%ROWCOUNT; + +END updatePerson; +---- +==== + +[[legacy-sql-load]] +=== Legacy custom SQL for loading + +You can also declare your own SQL (or HQL) queries for entity loading. +As with inserts, updates, and deletes, this can be done at the individual column level as described in +For columns, see <> or at the statement level. +Here is an example of a statement level override: + +[source,xml] +---- + + + SELECT NAME AS {pers.name}, ID AS {pers.id} + FROM PERSON + WHERE ID=? + FOR UPDATE + +---- + +This is just a named query declaration, as discussed earlier. You can reference this named query in a class mapping: + +[source,xml] +---- + + + + + + + +---- + +This even works with stored procedures. + +You can even define a query for collection loading: + +[source,xml] +---- + + + + + +---- + +[source,xml] +---- + + + SELECT {emp.*} + FROM EMPLOYMENT emp + WHERE EMPLOYER = :id + ORDER BY STARTDATE ASC, EMPLOYEE ASC + +---- + +You can also define an entity loader that loads a collection by join fetching: + +[source,xml] +---- + + + + SELECT NAME AS {pers.*}, {emp.*} + FROM PERSON pers + LEFT OUTER JOIN EMPLOYMENT emp + ON pers.ID = emp.PERSON_ID + WHERE ID=? + +---- + +The annotation equivalent `` is the `@Loader` annotation as seen in <>. \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/query-native/Native.adoc b/documentation/src/main/asciidoc/userguide/chapters/query-native/Native.adoc index 97da42f2f9..77c1ab5a32 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/query-native/Native.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/query-native/Native.adoc @@ -1,190 +1,307 @@ -[[querynative]] +[[sql]] == Native SQL Queries -:sourcedir: extras +:modeldir: ../../../../../main/java/org/hibernate/userguide/model +:sourcedir: ../../../../../test/java/org/hibernate/userguide/sql +:extrasdir: extras You may also express queries in the native SQL dialect of your database. -This is useful if you want to utilize database specific features such as query hints or the `CONNECT BY` option in Oracle. +This is useful if you want to utilize database specific features such as window functions, Common Table Expressions (CTE) or the `CONNECT BY` option in Oracle. It also provides a clean migration path from a direct SQL/JDBC based application to Hibernate/JPA. -Hibernate also allows you to specify handwritten SQL (including stored procedures) for all create, update, delete, and load operations. +Hibernate also allows you to specify handwritten SQL (including stored procedures) for all create, update, delete, and retrieve operations. -[[querynative-creating]] -=== Using a `SQLQuery` +[[sql-jpa-query]] +=== Creating a native query using JPA Execution of native SQL queries is controlled via the `SQLQuery` interface, which is obtained by calling `Session.createSQLQuery()`. The following sections describe how to use this API for querying. +[[sql-scalar-query]] === Scalar queries -The most basic SQL query is to get a list of scalars (values). +The most basic SQL query is to get a list of scalars (column) values. -[source,java] +[[sql-jpa-all-columns-scalar-query-example]] +.JPA native query selecting all columns +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT * FROM CATS" ) - .list(); - -session - .createSQLQuery( "SELECT ID, NAME, BIRTHDATE FROM CATS" ) - .list(); +include::{sourcedir}/SQLTest.java[tags=sql-jpa-all-columns-scalar-query-example] ---- +==== -These will return a List of Object arrays ( `Object[]` ) with scalar values for each column in the `CATS` table. -Hibernate will use `ResultSetMetadata` to deduce the actual order and types of the returned scalar values. +[[sql-jpa-custom-column-selection-scalar-query-example]] +.JPA native query with a custom column selection +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-jpa-custom-column-selection-scalar-query-example] +---- +==== + +[[sql-hibernate-all-columns-scalar-query-example]] +.Hibernate native query selecting all columns +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-all-columns-scalar-query-example] +---- +==== + +[[sql-hibernate-custom-column-selection-scalar-query-example]] +.Hibernate native query with a custom column selection +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-custom-column-selection-scalar-query-example] +---- +==== + +These will return a `List` of `Object` arrays ( `Object[]` ) with scalar values for each column in the `PERSON` table. +Hibernate will use `java.sql.ResultSetMetadata` to deduce the actual order and types of the returned scalar values. To avoid the overhead of using `ResultSetMetadata`, or simply to be more explicit in what is returned, one can use `addScalar()`: -[source,java] +[[sql-hibernate-scalar-query-explicit-result-set-example]] +.Hibernate native query with explicit result set selection +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery("SELECT * FROM CATS" ) - .addScalar( "ID", Hibernate.LONG ) - .addScalar( "NAME", Hibernate.STRING ) - .addScalar( "BIRTHDATE", Hibernate.DATE ) +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-scalar-query-explicit-result-set-example] ---- +==== -This query specified: - -* the SQL query string -* the columns and types to return - -Although it still returns an Object arrays, this query will not use the `ResultSetMetadata` anymore since it explicitly get the `ID`, `NAME` and `BIRTHDATE` columns as respectively a `Long`, `String `and a `Short` from the underlying `ResultSet`. -This also means that only these three columns will be returned, even though the query is using `*` and the `ResultSet` contains more than the three listed columns. +Although it still returns an `Object` arrays, this query will not use the `ResultSetMetadata` anymore since it explicitly gets the `id` and `name` columns as respectively a `BigInteger` and a `String` from the underlying `ResultSet`. +This also means that only these two columns will be returned, even though the query is still using `*` and the `ResultSet` contains more than the three listed columns. It is possible to leave out the type information for all or some of the scalars. -[source,java] +[[sql-hibernate-scalar-query-partial-explicit-result-set-example]] +.Hibernate native query with result set selection that's a partially explicit +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT * FROM CATS" ) - .addScalar( "ID", Hibernate.LONG ) - .addScalar( "NAME" ) - .addScalar( "BIRTHDATE" ) +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-scalar-query-partial-explicit-result-set-example] ---- +==== -This is essentially the same query as before, but now `ResultSetMetaData` is used to determine the type of `NAME` and `BIRTHDATE`, where as the type of `ID` is explicitly specified. +This is essentially the same query as before, but now `ResultSetMetaData` is used to determine the type of `name`, where as the type of `id` is explicitly specified. -How the `java.sql.Types` returned from `ResultSetMetaData` is mapped to Hibernate types is controlled by the Dialect. +How the `java.sql.Types` returned from `ResultSetMetaData` is mapped to Hibernate types is controlled by the `Dialect`. If a specific type is not mapped, or does not result in the expected type, it is possible to customize it via calls to `registerHibernateType` in the Dialect. +[[sql-entity-query]] === Entity queries -The above queries were all about returning scalar values, basically returning the "raw" values from the `ResultSet`. -The following shows how to get entity objects from a native sql query via `addEntity()`. +The above queries were all about returning scalar values, basically returning the _raw_ values from the `ResultSet`. -[source,java] +[[sql-jpa-entity-query-example]] +.JPA native query selecting entities +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT * FROM CATS" ) - .addEntity( Cat.class ); - -session - .createSQLQuery( "SELECT ID, NAME, BIRTHDATE FROM CATS" ) - .addEntity( Cat.class ); +include::{sourcedir}/SQLTest.java[tags=sql-jpa-entity-query-example] ---- +==== -This query specified: - -* the SQL query string -* the entity returned by the query - -Assuming that `Cat` is mapped as a class with the columns `ID`, `NAME` and `BIRTHDATE` the above queries will both return a `List` where each element is a `Cat` entity. - -If the entity is mapped with a `many-to-one` to another entity it is required to also return this when performing the native query, otherwise a database specific "column not found" error will occur. -The additional columns will automatically be returned when using the * notation, but we prefer to be explicit as in the following example for a `many-to-one` to a `Dog`: - -[source,java] +[[sql-hibernate-entity-query-example]] +.Hibernate native query selecting entities +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT ID, NAME, BIRTHDATE, DOG_ID FROM CATS" ) - .addEntity( Cat.class ); +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-query-example] ---- +==== -This will allow `cat.getDog()` to function properly. +Assuming that `Person` is mapped as a class with the columns `id`, `name`, `nickName`, `address`, `createdOn` and `version`, +the following query will also return a `List` where each element is a `Person` entity. +[[sql-jpa-entity-query-explicit-result-set-example]] +.JPA native query selecting entities with explicit result set +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-jpa-entity-query-explicit-result-set-example] +---- +==== + +[[sql-hibernate-entity-query-explicit-result-set-example]] +.Hibernate native query selecting entities with explicit result set +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-query-explicit-result-set-example] +---- +==== + +[[sql-entity-associations-query]] === Handling associations and collections -It is possible to eagerly join in the `Dog` to avoid the possible extra roundtrip for initializing the proxy. -This is done via the `addJoin()` method, which allows you to join in an association or collection. +If the entity is mapped with a `many-to-one` or a child-side `one-to-one` to another entity, it is required to also return this when performing the native query, +otherwise a database specific _column not found_ error will occur. -[source,java] +[[sql-jpa-entity-associations-query-many-to-one-example]] +.JPA native query selecting entities with many-to-one association +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT c.ID, NAME, BIRTHDATE, DOG_ID, D_ID, D_NAME FROM CATS c, DOGS d WHERE c.DOG_ID = d.D_ID" ) - .addEntity( "cat", Cat.class ) - .addJoin( "cat.dog" ); +include::{sourcedir}/SQLTest.java[tags=sql-jpa-entity-associations-query-many-to-one-example] +---- +==== + +[[sql-hibernate-entity-associations-query-many-to-one-example]] +.Hibernate native query selecting entities with many-to-one association +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-associations-query-many-to-one-example] +---- +==== + +This will allow the `Phone#person` to function properly. + +[NOTE] +==== +The additional columns will automatically be returned when using the `*` notation. +==== + +It is possible to eagerly join the `Phone` and the `Person` entities to avoid the possible extra roundtrip for initializing the `many-to-one` association. + +[[sql-jpa-entity-associations-query-many-to-one-join-example]] +.JPA native query selecting entities with joined many-to-one association +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-jpa-entity-associations-query-many-to-one-join-example] ---- -In this example, the returned `Cat`s will have their `dog` property fully initialized without any extra roundtrip to the database. -Notice that you added an alias name _"cat"_ to be able to specify the target property path of the join. -It is possible to do the same eager joining for collections (e.g. if the `Cat` had a `one-to-many` to `Dog` instead). +[source, SQL, indent=0] +---- +include::{extrasdir}/sql-jpa-entity-associations-query-many-to-one-join-example.sql[] +---- +==== -[source,java] +[[sql-hibernate-entity-associations-query-many-to-one-join-example]] +.Hibernate native query selecting entities with joined many-to-one association +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT ID, NAME, BIRTHDATE, D_ID, D_NAME, CAT_ID FROM CATS c, DOGS d WHERE c.ID = d.CAT_ID" ) - .addEntity( "cat", Cat.class ) - .addJoin( "cat.dogs" ); +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-associations-query-many-to-one-join-example] ---- +[source, SQL, indent=0] +---- +include::{extrasdir}/sql-hibernate-entity-associations-query-many-to-one-join-example.sql[] +---- +==== + +[NOTE] +==== +As seen in the associated SQL query, Hibernate manages to construct the entity hierarchy without requiring any extra database roundtrip. +==== + +By default, when using the `addJoin()` method, the result set will contain both entities that are joined. +To construct the entity hierarchy, you need to use a `ROOT_ENTITY` or `DISTINCT_ROOT_ENTITY` `ResultTransformer`. + +[[sql-hibernate-entity-associations-query-many-to-one-join-result-transformer-example]] +.Hibernate native query selecting entities with joined many-to-one association and `ResultTransformer` +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-associations-query-many-to-one-join-result-transformer-example] +---- +==== + +[NOTE] +==== +Because of the `ROOT_ENTITY` `ResultTransformer`, this query will return the parent-side as root entities. +==== + +Notice that you added an alias name _pr_ to be able to specify the target property path of the join. +It is possible to do the same eager joining for collections (e.g. the `Phone#calls` `one-to-many` association). + +[[sql-jpa-entity-associations-query-one-to-many-join-example]] +.JPA native query selecting entities with joined one-to-many association +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-jpa-entity-associations-query-one-to-many-join-example] +---- + +[source, SQL, indent=0] +---- +include::{extrasdir}/sql-jpa-entity-associations-query-one-to-many-join-example.sql[] +---- +==== + +[[sql-hibernate-entity-associations-query-one-to-many-join-example]] +.Hibernate native query selecting entities with joined one-to-many association +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-associations-query-one-to-many-join-example] +---- + +[source, SQL, indent=0] +---- +include::{extrasdir}/sql-hibernate-entity-associations-query-one-to-many-join-example.sql[] +---- +==== + At this stage you are reaching the limits of what is possible with native queries, without starting to enhance the sql queries to make them usable in Hibernate. Problems can arise when returning multiple entities of the same type or when the default alias/column names are not enough. +[[sql-multi-entity-query]] === Returning multiple entities Until now, the result set column names are assumed to be the same as the column names specified in the mapping document. This can be problematic for SQL queries that join multiple tables, since the same column names can appear in more than one table. -Column alias injection is needed in the following query (which most likely will fail): +Column alias injection is needed in the following query which otherwise throws `NonUniqueDiscoveredSqlAliasException`. -[source,java] +[[sql-jpa-multi-entity-query-example]] +.JPA native query selecting entities with the same column names +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT c.*, m.* FROM CATS c, CATS m WHERE c.MOTHER_ID = c.ID" ) - .addEntity( "cat", Cat.class) - .addEntity( "mother", Cat.class) +include::{sourcedir}/SQLTest.java[tags=sql-jpa-multi-entity-query-example] ---- +==== -The query was intended to return two Cat instances per row: a cat and its mother. -The query will, however, fail because there is a conflict of names since the instances are mapped to the same column names. -Also, on some databases the returned column aliases will most likely be on the form `c.ID`, `c.NAME`, etc. -which are not equal to the columns specified in the mappings (`ID` and `NAME`). +[[sql-hibernate-multi-entity-query-example]] +.Hibernate native query selecting entities with the same column names +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-multi-entity-query-example] +---- +==== + +The query was intended to return all `Person` and `Partner` instances with the same name. +The query fails because there is a conflict of names since the two entities are mapped to the same column names (e.g. `id`, `name`, `version`). +Also, on some databases the returned column aliases will most likely be on the form `pr.id`, `pr.name`, etc. +which are not equal to the columns specified in the mappings (`id` and `name`). The following form is not vulnerable to column name duplication: -[source,java] +[[sql-hibernate-multi-entity-query-alias-example]] +.Hibernate native query selecting entities with the same column names and aliases +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT {cat.*}, {m.*} FROM CATS c, CATS m WHERE c.MOTHER_ID = m.ID" ) - .addEntity( "cat", Cat.class ) - .addEntity( "mother", Cat.class ) +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-multi-entity-query-alias-example] ---- +==== -This query specified: +[NOTE] +==== +There's no such equivalent in JPA because the `Query` interface doesn't define an `addEntity` method equivalent. +==== -* the SQL query string, with placeholders for Hibernate to inject column aliases -* the entities returned by the query - -The `{cat.*}` and `{mother.*}` notation used above is a shorthand for "all properties". +The `{pr.*}` and `{pt.*}` notation used above is a shorthand for "all properties". Alternatively, you can list the columns explicitly, but even in this case Hibernate injects the SQL column aliases for each property. The placeholder for a column alias is just the property name qualified by the table alias. -In the following example, you retrieve `Cats` and their mothers from a different table (cat_log) to the one declared in the mapping metadata. -You can even use the property aliases in the where clause. -[source,java] ----- -String sql = - "SELECT ID as {c.id}, NAME as {c.name}, " + - "BIRTHDATE as {c.birthDate}, MOTHER_ID as {c.mother}, {mother.*} " + - "FROM CAT_LOG c, CAT_LOG m WHERE {c.mother} = c.ID"; - -List loggedCats = session - .createSQLQuery( sql ) - .addEntity( "cat", Cat.class ) - .addEntity( "mother", Cat.class ) - .list() ----- - -[[querysql-aliasreferences]] +[[sql-alias-references]] === Alias and property references In most cases the above alias injection is needed. @@ -224,475 +341,306 @@ Please note that the alias names in the result are simply examples, each alias w |All properties of the collection |`{[aliasname].*}` |`{coll.*}` |======================================================================= -=== Returning non-managed entities +[[sql-dto-query]] +=== Returning DTOs (Data Transfer Objects) -It is possible to apply a ResultTransformer to native SQL queries, allowing it to return non-managed entities. +It is possible to apply a `ResultTransformer` to native SQL queries, allowing it to return non-managed entities. -[source,java] +[[sql-hibernate-dto-query-example]] +.Hibernate native query selecting DTOs +==== +[source, JAVA, indent=0] ---- -session - .createSQLQuery( "SELECT NAME, BIRTHDATE FROM CATS" ) - .setResultTransformer( Transformers.aliasToBean( CatDTO.class ) ) +include::{sourcedir}/PersonSummaryDTO.java[tags=sql-hibernate-dto-query-example, indent=0] + +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-dto-query-example, indent=0] ---- +==== -This query specified: +[NOTE] +==== +There's no such equivalent in JPA because the `Query` interface doesn't define a `setResultTransformer` method equivalent. +==== -* the SQL query string -* a result transformer - -The above query will return a list of `CatDTO` which has been instantiated and injected the values of `NAME` and `BIRTHNAME` into its corresponding properties or fields. +The above query will return a list of `PersonSummaryDTO` which has been instantiated and injected the values of `id` and `name` into its corresponding properties or fields. +[[sql-inheritance-query]] === Handling inheritance Native SQL queries which query for entities that are mapped as part of an inheritance must include all properties for the base class and all its subclasses. +[[sql-hibernate-inheritance-query-example]] +.Hibernate native query selecting subclasses +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-inheritance-query-example] +---- +==== + +[NOTE] +==== +There's no such equivalent in JPA because the `Query` interface doesn't define an `addEntity` method equivalent. +==== + +[[sql-query-parameters]] === Parameters Native SQL queries support positional as well as named parameters: -[source,java] +[[sql-jpa-query-parameters-example]] +.JPA native query with parameters +==== +[source, JAVA, indent=0] ---- -Query query = session - .createSQLQuery( "SELECT * FROM CATS WHERE NAME like ?" ) - .addEntity( Cat.class ); - -List pusList = query - .setString( 0, "Pus%" ) - .list(); - -query = session - .createSQLQuery( "SELECT * FROM CATS WHERE NAME like :name" ) - .addEntity( Cat.class); - -List pusList = query - .setString( "name", "Pus%" ) - .list(); +include::{sourcedir}/SQLTest.java[tags=sql-jpa-query-parameters-example] ---- +==== -[[querysql-namedqueries]] +[[sql-hibernate-query-parameters-example]] +.Hibernate native query with parameters +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-query-parameters-example] +---- +==== + +[[sql-named-queries]] === Named SQL queries -Named SQL queries can also be defined in the mapping document and called in exactly the same way as a named HQL query. -In this case, you do _not_ need to call `addEntity()`. +Named SQL queries can also be defined during mapping and called in exactly the same way as a named HQL query. +In this case, you do _not_ need to call `addEntity()` anymore. -.Named sql query using the `` mapping element +JPA defines the `javax.persistence.NamedNativeQuery` annotation for this purpose, +and the Hibernate `org.hibernate.annotations.NamedNativeQuery` annotation extends it and adds the following attributes: + +`flushMode()`:: + The flush mode for the query. By default, it uses the current Persistence Context flush mode. +`cacheable()`:: + Whether the query (results) is cacheable or not. By default, queries are not cached. +`cacheRegion()`:: + If the query results are cacheable, name the query cache region to use. +`fetchSize()`:: + The number of rows fetched by the JDBC Driver per database trip. The default value is given by the JDBC driver. +`timeout()`:: + The query timeout (in seconds). By default, there's no timeout. +`callable()`:: + Does the SQL query represent a call to a procedure/function? Default is false. +`comment()`:: + A comment added to the SQL query for tuning the execution plan. +`cacheMode()`:: + The cache mode used for this query. This refers to entities/collections returned by the query. + The default value is `CacheModeType.NORMAL`. +`readOnly()`:: + Whether the results should be read-only. By default, queries are not read-only so entities are stored in the Persistence Context. + +[[sql-scalar-named-queries]] +==== Named SQL queries selecting scalar values + +To fetch a single column of given table, the named query looks as follows: + +[[sql-scalar-NamedNativeQuery-example]] +.Single scalar value `NamedNativeQuery` ==== -[source,xml] +[source, JAVA, indent=0] ---- - - - SELECT person.NAME AS {person.name}, - person.AGE AS {person.age}, - person.SEX AS {person.sex} - FROM PERSON person - WHERE person.NAME LIKE :namePattern - +include::{modeldir}/Person.java[tags=sql-scalar-NamedNativeQuery-example] ---- ==== -.Execution of a named query +[[sql-jpa-scalar-named-query-example]] +.JPA named native query selecting a scalar value ==== -[source,java] +[source, JAVA, indent=0] ---- -List people = session - .getNamedQuery( "persons" ) - .setString( "namePattern", namePattern ) - .setMaxResults( 50 ) - .list(); +include::{sourcedir}/SQLTest.java[tags=sql-jpa-scalar-named-query-example] ---- ==== -The `` element is use to join associations and the `` element is used to define queries which initialize collections. - -.Named sql query with association +[[sql-hibernate-scalar-named-query-example]] +.Hibernate named native query selecting a scalar value ==== -[source,xml] +[source, JAVA, indent=0] ---- - - - - SELECT person.NAME AS {person.name}, - person.AGE AS {person.age}, - person.SEX AS {person.sex}, - address.STREET AS {address.street}, - address.CITY AS {address.city}, - address.STATE AS {address.state}, - address.ZIP AS {address.zip} - FROM PERSON person - JOIN ADDRESS address - ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' - WHERE person.NAME LIKE :namePattern - +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-scalar-named-query-example] ---- ==== -A named SQL query may return a scalar value. -You must declare the column alias and Hibernate type using the `` element: +Selecting multiple scalar values is done like this: -.Named query returning a scalar +[[sql-multiple-scalar-values-NamedNativeQuery-example]] +.Multiple scalar values `NamedNativeQuery` ==== -[source,xml] +[source, JAVA, indent=0] ---- - - - - SELECT p.NAME AS name, - p.AGE AS age, - FROM PERSON p WHERE p.NAME LIKE 'Hiber%' - +include::{modeldir}/Person.java[tags=sql-multiple-scalar-values-NamedNativeQuery-example] ---- ==== -You can externalize the resultset mapping information in a `` element which will allow you to either reuse them across several named queries or through the `setResultSetMapping()` API. +Without specifying an explicit result type, Hibernate will assume an `Object` array: -. mapping used to externalize mappinginformation +[[sql-jpa-multiple-scalar-values-named-query-example]] +.JPA named native query selecting multiple scalar values ==== -[source,xml] +[source, JAVA, indent=0] ---- - - - - - - - SELECT person.NAME AS {person.name}, - person.AGE AS {person.age}, - person.SEX AS {person.sex}, - address.STREET AS {address.street}, - address.CITY AS {address.city}, - address.STATE AS {address.state}, - address.ZIP AS {address.zip} - FROM PERSON person - JOIN ADDRESS address - ON person.ID = address.PERSON_ID AND address.TYPE='MAILING' - WHERE person.NAME LIKE :namePattern - +include::{sourcedir}/SQLTest.java[tags=sql-jpa-multiple-scalar-values-named-query-example] ---- ==== -You can, alternatively, use the resultset mapping information in your hbm files directly in java code. - -.Programmatically specifying the result mapping information +[[sql-hibernate-multiple-scalar-values-named-query-example]] +.Hibernate named native query selecting multiple scalar values ==== -[source,java] +[source, JAVA, indent=0] ---- -List cats = session - .createSQLQuery( "select {cat.*}, {kitten.*} from cats cat, cats kitten where kitten.mother = cat.id" ) - .setResultSetMapping("catAndKitten") - .list(); +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-multiple-scalar-values-named-query-example] ---- ==== -So far we have only looked at externalizing SQL queries using Hibernate mapping files. -The same concept is also available with annotations and is called named native queries. -You can use `@NamedNativeQuery`/`@NamedNativeQueries in conjunction with `@SqlResultSetMapping`/`@SqlResultSetMappings`. +It's possible to use a DTO to store the resulting scalar values: -Like `@NamedQuery`, `@NamedNativeQuery` and `@SqlResultSetMapping` can be defined at class level, but their scope is global to the application. +[[sql-ConstructorResult-dto-example]] +.DTO to store multiple scalar values +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/PersonNames.java[tags=sql-ConstructorResult-dto-example] +---- +==== -Let's look at a view examples. +[[sql-multiple-scalar-values-dto-NamedNativeQuery-example]] +.Multiple scalar values `NamedNativeQuery` with `ConstructorResult` +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/Person.java[tags=sql-multiple-scalar-values-dto-NamedNativeQuery-example] +---- +==== -<> shows how a `resultSetMapping` parameter is defined in `@NamedNativeQuery`. -It represents the name of a defined `@SqlResultSetMapping`. +[[sql-jpa-multiple-scalar-values-dto-named-query-example]] +.JPA named native query selecting multiple scalar values into a DTO +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-jpa-multiple-scalar-values-dto-named-query-example] +---- +==== + +[[sql-hibernate-multiple-scalar-values-dto-named-query-example]] +.Hibernate named native query selecting multiple scalar values into a DTO +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-multiple-scalar-values-dto-named-query-example] +---- +==== + +[[sql-entity-named-queries]] +==== Named SQL queries selecting entities + +Considering the following named query: + +[[sql-entity-NamedNativeQuery-example]] +.Single-entity `NamedNativeQuery` +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/Person.java[tags=sql-entity-NamedNativeQuery-example] +---- +==== The result set mapping declares the entities retrieved by this native query. Each field of the entity is bound to an SQL alias (or column name). All fields of the entity including the ones of subclasses and the foreign key columns of related entities have to be present in the SQL query. Field definitions are optional provided that they map to the same column name as the one declared on the class property. -In the example two entities, `Night` and `Area`, are returned, and each property is declared and associated to a column name, actually the column name retrieved by the query. +Executing this named native query can be done as follows: -[[example-named-native-query-annotation-with-result-set-mapping>]] -.Named SQL query using `@NamedNativeQuery` together with `@SqlResultSetMapping` +[[sql-jpa-entity-named-query-example]] +.JPA named native entity query ==== -[source,java] +[source, JAVA, indent=0] ---- -@NamedNativeQuery( - name = "night&area", - query = "select night.id nid, night.night_duration, " - + " night.night_date, area.id aid, night.area_id, area.name " - + "from Night night, Area area where night.area_id = area.id", - resultSetMapping="joinMapping") -@SqlResultSetMapping( - name = "joinMapping", - entities = { - @EntityResult(entityClass=Night.class, fields = { - @FieldResult(name = "id", column = "nid"), - @FieldResult(name = "duration", column = "night_duration"), - @FieldResult(name = "date", column = "night_date"), - @FieldResult(name = "area", column = "area_id"), - discriminatorColumn="disc" - }), - @EntityResult( - entityClass = Area.class, - fields = { - @FieldResult(name = "id", column = "aid"), - @FieldResult(name = "name", column = "name") - } - ) - } -) +include::{sourcedir}/SQLTest.java[tags=sql-jpa-entity-named-query-example] ---- ==== -In <> the result set mapping is implicit. -We only describe the entity class of the result set mapping. -The property / column mappings is done using the entity mapping values. -In this case the model property is bound to the model_txt column. - -[[example-implicit-result-set-mapping]] -.Implicit result set mapping +[[sql-hibernate-entity-named-query-example]] +.Hibernate named native entity query ==== -[source,java] +[source, JAVA, indent=0] ---- -@Entity -@SqlResultSetMapping( - name = "implicit", - entities = @EntityResult(entityClass=SpaceShip.class) -) -@NamedNativeQuery( - name = "implicitSample", - query="select * from SpaceShip", - resultSetMapping="implicit" -) -public class SpaceShip { - private String name; - private String model; - private double speed; +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-named-query-example] +---- +==== - @Id - public String getName() { - return name; - } +To join multiple entities, you need to use a `SqlResultSetMapping` for each entity the SQL query is going to fetch. - public void setName(String name) { - this.name = name; - } +[[sql-entity-associations-NamedNativeQuery-example]] +.Joined-entities `NamedNativeQuery` +==== +[source, JAVA, indent=0] +---- +include::{modeldir}/Person.java[tags=sql-entity-associations-NamedNativeQuery-example] +---- +==== - @Column(name = "model_txt") - public String getModel() { - return model; - } +[[sql-jpa-entity-associations_named-query-example]] +.JPA named native entity query with joined associations +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-jpa-entity-associations_named-query-example] +---- +==== - public void setModel(String model) { - this.model = model; - } - - public double getSpeed() { - return speed; - } - - public void setSpeed(double speed) { - this.speed = speed; - } -} +[[sql-hibernate-entity-associations_named-query-example]] +.Hibernate named native entity query with joined associations +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-entity-associations_named-query-example] ---- ==== Finally, if the association to a related entity involve a composite primary key, a `@FieldResult` element should be used for each foreign key column. The `@FieldResult` name is composed of the property name for the relationship, followed by a dot ("."), followed by the name or the field or property of the primary key. -This can be seen in <>. +For this example, the following entities are going to be used: -[[example-field-result-annotation-with-associations]] -.Using dot notation in @FieldResult for specifying associations +[[sql-composite-key-entity-associations_named-query-example]] +.Entity associations with composite keys and named native queries ==== -[source,java] +[source, JAVA, indent=0] ---- -@Entity -@SqlResultSetMapping( - name = "compositekey", - entities = @EntityResult( - entityClass=SpaceShip.class, - fields = { - @FieldResult(name = "name", column = "name"), - @FieldResult(name = "model", column = "model"), - @FieldResult(name = "speed", column = "speed"), - @FieldResult(name = "captain.firstname", column = "firstn"), - @FieldResult(name = "captain.lastname", column = "lastn"), - @FieldResult(name = "dimensions.length", column = "length"), - @FieldResult(name = "dimensions.width", column = "width") - } - ), - columns = { - @ColumnResult(name = "surface"), - @ColumnResult(name = "volume") - } - ) -) -@NamedNativeQuery( - name = "compositekey", - query = "select name, model, speed, lname as lastn, fname as firstn, length, width, length * width as surface from SpaceShip", - resultSetMapping = "compositekey" -) -public class SpaceShip { - private String name; - private String model; - private double speed; - private Captain captain; - private Dimensions dimensions; +include::{sourcedir}/Dimensions.java[tags=sql-composite-key-entity-associations_named-query-example, indent=0] - @Id - public String getName() { - return name; - } +include::{sourcedir}/Identity.java[tags=sql-composite-key-entity-associations_named-query-example, indent=0] - public void setName(String name) { - this.name = name; - } +include::{sourcedir}/Captain.java[tags=sql-composite-key-entity-associations_named-query-example, indent=0] - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumns( { - @JoinColumn(name = "fname", referencedColumnName = "firstname"), - @JoinColumn(name = "lname", referencedColumnName = "lastname") - } ) - public Captain getCaptain() { - return captain; - } - - public void setCaptain(Captain captain) { - this.captain = captain; - } - - public String getModel() { - return model; - } - - public void setModel(String model) { - this.model = model; - } - - public double getSpeed() { - return speed; - } - - public void setSpeed(double speed) { - this.speed = speed; - } - - public Dimensions getDimensions() { - return dimensions; - } - - public void setDimensions(Dimensions dimensions) { - this.dimensions = dimensions; - } -} - -@Entity -@IdClass(Identity.class) -public class Captain implements Serializable { - private String firstname; - private String lastname; - - @Id - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - @Id - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } -} +include::{sourcedir}/SpaceShip.java[tags=sql-composite-key-entity-associations_named-query-example, indent=0] ---- ==== -[TIP] +[[sql-jpa-composite-key-entity-associations_named-query-example]] +.JPA named native entity query with joined associations and composite keys ==== -If you retrieve a single entity using the default mapping, you can specify the `resultClass` attribute instead of `resultSetMapping`: - -[source,java] +[source, JAVA, indent=0] ---- -@NamedNativeQuery( - name = "implicitSample", - query = "select * from SpaceShip", - resultClass = SpaceShip.class -) -public class SpaceShip { - ... -} +include::{sourcedir}/SQLTest.java[tags=sql-jpa-composite-key-entity-associations_named-query-example] ---- ==== -In some of your native queries, you'll have to return scalar values, for example when building report queries. -You can map them in the `@SqlResultsetMapping` through `@ColumnResult`. -You actually can even mix, entities and scalar returns in the same native query (this is probably not that common though). - -.Scalar values via `@ColumnResult` +[[sql-hibernate-composite-key-entity-associations_named-query-example]] +.Hibernate named native entity query with joined associations and composite keys ==== -[source,java] +[source, JAVA, indent=0] ---- -@SqlResultSetMapping( - name = "scalar", - columns = @ColumnResult( - name = "dimension" - ) -) -@NamedNativeQuery( - name = "scalar", - query = "select length*width as dimension from SpaceShip", - resultSetMapping = "scalar" -) +include::{sourcedir}/SQLTest.java[tags=sql-hibernate-composite-key-entity-associations_named-query-example] ---- ==== -An other query hint specific to native queries has been introduced: `org.hibernate.callable` which can be true or false depending on whether the query is a stored procedure or not. - -[[propertyresults]] -=== Using return-property to explicitly specify column/alias names - -You can explicitly tell Hibernate what column aliases to use with ``, instead of using the `{}` syntax to let Hibernate inject its own aliases. -For example: - -[source,xml] ----- - - - - - - - SELECT person.NAME AS myName, - person.AGE AS myAge, - person.SEX AS mySex, - FROM PERSON person WHERE person.NAME LIKE :name - ----- - -`` also works with multiple columns. -This solves a limitation with the `{}` syntax which cannot allow fine grained control of multi-column properties. - -[source,xml] ----- - - - - - - - - - SELECT EMPLOYEE AS {emp.employee}, EMPLOYER AS {emp.employer}, - STARTDATE AS {emp.startDate}, ENDDATE AS {emp.endDate}, - REGIONCODE as {emp.regionCode}, EID AS {emp.id}, VALUE, CURRENCY - FROM EMPLOYMENT - WHERE EMPLOYER = :id AND ENDDATE IS NULL - ORDER BY STARTDATE ASC - ----- - -In this example `` was used in combination with the `{}` syntax for injection. -This allows users to choose how they want to refer column and properties. - -If your mapping has a discriminator you must use `` to specify the discriminator column. - [[sp_query]] === Using stored procedures for querying @@ -741,7 +689,7 @@ To use this query in Hibernate you need to map it via a named query. Stored procedures currently only return scalars and entities. `` and `` are not supported. -[[querysql-limits-storedprocedures]] +[[sql-limits-storedprocedures]] === Rules/limitations for using stored procedures You cannot use stored procedures with Hibernate unless you follow some procedure/function rules. @@ -770,7 +718,7 @@ Note that since these servers can return multiple result sets and update counts, Everything else will be discarded. * If you can enable `SET NOCOUNT ON` in your procedure it will probably be more efficient, but this is not a requirement. -[[querysql-cud]] +[[sql-cud]] === Custom SQL for create, update and delete Hibernate can use custom SQL for create, update, and delete operations. @@ -782,7 +730,7 @@ The following example shows how to define custom SQL operations using annotation [[example-custom-crdu-via-annotations]] .Custom CRUD via annotations ==== -[source,java] +[source, JAVA, indent=0] ---- @Entity @Table(name = "CHAOS") @@ -837,7 +785,7 @@ You can use the exact same set of annotations respectively xml nodes to override .Overriding SQL statements for collections using annotations ==== -[source,java] +[source, JAVA, indent=0] ---- @OneToMany @JoinColumn(name = "chaos_fk") @@ -859,7 +807,7 @@ Overriding SQL statements for secondary tables is also possible using `@org.hibe .Overriding SQL statements for secondary tables ==== -[source,java] +[source, JAVA, indent=0] ---- @Entity @SecondaryTables({ @@ -913,7 +861,7 @@ END updatePerson; ---- ==== -[[querysql-load]] +[[sql-load]] === Custom SQL for loading You can also declare your own SQL (or HQL) queries for entity loading. diff --git a/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-hibernate-entity-associations-query-many-to-one-join-example.sql b/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-hibernate-entity-associations-query-many-to-one-join-example.sql new file mode 100644 index 0000000000..a38f8a1a64 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-hibernate-entity-associations-query-many-to-one-join-example.sql @@ -0,0 +1,5 @@ +SELECT id , + number , + type , + person_id +FROM phone \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-hibernate-entity-associations-query-one-to-many-join-example.sql b/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-hibernate-entity-associations-query-one-to-many-join-example.sql new file mode 100644 index 0000000000..c594dde8b6 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-hibernate-entity-associations-query-one-to-many-join-example.sql @@ -0,0 +1,3 @@ +SELECT * +FROM phone ph +JOIN call c ON c.phone_id = ph.id \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-jpa-entity-associations-query-many-to-one-join-example.sql b/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-jpa-entity-associations-query-many-to-one-join-example.sql new file mode 100644 index 0000000000..a38f8a1a64 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-jpa-entity-associations-query-many-to-one-join-example.sql @@ -0,0 +1,5 @@ +SELECT id , + number , + type , + person_id +FROM phone \ No newline at end of file diff --git a/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-jpa-entity-associations-query-one-to-many-join-example.sql b/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-jpa-entity-associations-query-one-to-many-join-example.sql new file mode 100644 index 0000000000..c594dde8b6 --- /dev/null +++ b/documentation/src/main/asciidoc/userguide/chapters/query-native/extras/sql-jpa-entity-associations-query-one-to-many-join-example.sql @@ -0,0 +1,3 @@ +SELECT * +FROM phone ph +JOIN call c ON c.phone_id = ph.id \ No newline at end of file diff --git a/documentation/src/main/java/org/hibernate/userguide/model/AddressType.java b/documentation/src/main/java/org/hibernate/userguide/model/AddressType.java index c3846b124c..b5bc887e59 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/AddressType.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/AddressType.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; /** diff --git a/documentation/src/main/java/org/hibernate/userguide/model/Call.java b/documentation/src/main/java/org/hibernate/userguide/model/Call.java index 9406ce7035..d3add2e0f6 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/Call.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/Call.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; import java.util.Date; @@ -10,7 +16,7 @@ import javax.persistence.ManyToOne; * @author Vlad Mihalcea */ //tag::hql-examples-domain-model-example[] -@Entity(name = "Call") +@Entity public class Call { @Id diff --git a/documentation/src/main/java/org/hibernate/userguide/model/CreditCardPayment.java b/documentation/src/main/java/org/hibernate/userguide/model/CreditCardPayment.java index 04222cf4bf..439ffdc54e 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/CreditCardPayment.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/CreditCardPayment.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; import javax.persistence.Entity; diff --git a/documentation/src/main/java/org/hibernate/userguide/model/Partner.java b/documentation/src/main/java/org/hibernate/userguide/model/Partner.java index 476b79f36b..7d515e6fbc 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/Partner.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/Partner.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; import javax.persistence.Entity; @@ -16,11 +22,11 @@ public class Partner { @GeneratedValue private Long id; + private String name; + @Version private int version; - private String name; - //Getters and setters are omitted for brevity //end::hql-examples-domain-model-example[] diff --git a/documentation/src/main/java/org/hibernate/userguide/model/Payment.java b/documentation/src/main/java/org/hibernate/userguide/model/Payment.java index 285409aa0e..81e4e9966e 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/Payment.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/Payment.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; import java.math.BigDecimal; @@ -5,6 +11,7 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Inheritance; +import javax.persistence.InheritanceType; import javax.persistence.ManyToOne; /** @@ -12,7 +19,7 @@ import javax.persistence.ManyToOne; */ //tag::hql-examples-domain-model-example[] @Entity -@Inheritance +@Inheritance(strategy = InheritanceType.JOINED) public class Payment { @Id diff --git a/documentation/src/main/java/org/hibernate/userguide/model/Person.java b/documentation/src/main/java/org/hibernate/userguide/model/Person.java index 3598f1bf69..17bc284a03 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/Person.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/Person.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; import java.util.ArrayList; @@ -6,16 +12,24 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import javax.persistence.CascadeType; +import javax.persistence.ColumnResult; +import javax.persistence.ConstructorResult; import javax.persistence.ElementCollection; import javax.persistence.Entity; +import javax.persistence.EntityResult; import javax.persistence.EnumType; +import javax.persistence.FieldResult; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.MapKeyEnumerated; +import javax.persistence.NamedNativeQueries; +import javax.persistence.NamedNativeQuery; import javax.persistence.NamedQueries; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OrderColumn; +import javax.persistence.SqlResultSetMapping; +import javax.persistence.SqlResultSetMappings; import javax.persistence.Temporal; import javax.persistence.TemporalType; import javax.persistence.Version; @@ -23,6 +37,115 @@ import javax.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.number AS \"ph.number\", " + + " ph.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[] +}) +@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[] +}) //tag::hql-examples-domain-model-example[] //tag::jpql-api-named-query-example[] @NamedQueries( diff --git a/documentation/src/main/java/org/hibernate/userguide/model/PersonNames.java b/documentation/src/main/java/org/hibernate/userguide/model/PersonNames.java new file mode 100644 index 0000000000..1053a77080 --- /dev/null +++ b/documentation/src/main/java/org/hibernate/userguide/model/PersonNames.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.userguide.model; + +/** + * @author Vlad Mihalcea + */ +//tag::sql-ConstructorResult-dto-example[] +public class PersonNames { + + private final String name; + + private final String nickName; + + public PersonNames(String name, String nickName) { + this.name = name; + this.nickName = nickName; + } + + public String getName() { + return name; + } + + public String getNickName() { + return nickName; + } +} +//end::sql-ConstructorResult-dto-example[] diff --git a/documentation/src/main/java/org/hibernate/userguide/model/Phone.java b/documentation/src/main/java/org/hibernate/userguide/model/Phone.java index 83e8a903fe..195f3e7d0d 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/Phone.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/Phone.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; import java.util.ArrayList; @@ -10,6 +16,7 @@ import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; +import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.MapKey; @@ -27,7 +34,7 @@ public class Phone { @Id private Long id; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) private Person person; private String number; diff --git a/documentation/src/main/java/org/hibernate/userguide/model/PhoneType.java b/documentation/src/main/java/org/hibernate/userguide/model/PhoneType.java index 47317c451f..3acbbe585c 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/PhoneType.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/PhoneType.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; /** diff --git a/documentation/src/main/java/org/hibernate/userguide/model/WireTransferPayment.java b/documentation/src/main/java/org/hibernate/userguide/model/WireTransferPayment.java index 9e6988707c..90529edcfe 100644 --- a/documentation/src/main/java/org/hibernate/userguide/model/WireTransferPayment.java +++ b/documentation/src/main/java/org/hibernate/userguide/model/WireTransferPayment.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.model; import javax.persistence.Entity; diff --git a/documentation/src/test/java/org/hibernate/userguide/criteria/PersonWrapper.java b/documentation/src/test/java/org/hibernate/userguide/criteria/PersonWrapper.java index 91dac03802..2c9f6f7614 100644 --- a/documentation/src/test/java/org/hibernate/userguide/criteria/PersonWrapper.java +++ b/documentation/src/test/java/org/hibernate/userguide/criteria/PersonWrapper.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.criteria; /** diff --git a/documentation/src/test/java/org/hibernate/userguide/hql/CallStatistics.java b/documentation/src/test/java/org/hibernate/userguide/hql/CallStatistics.java index eb57f40d73..757693c023 100644 --- a/documentation/src/test/java/org/hibernate/userguide/hql/CallStatistics.java +++ b/documentation/src/test/java/org/hibernate/userguide/hql/CallStatistics.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.hql; /** diff --git a/documentation/src/test/java/org/hibernate/userguide/locking/ExplicitLockingTest.java b/documentation/src/test/java/org/hibernate/userguide/locking/ExplicitLockingTest.java index 870f612e0d..13147bfbe0 100644 --- a/documentation/src/test/java/org/hibernate/userguide/locking/ExplicitLockingTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/locking/ExplicitLockingTest.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.locking; import java.util.ArrayList; diff --git a/documentation/src/test/java/org/hibernate/userguide/locking/OptimisticLockingTest.java b/documentation/src/test/java/org/hibernate/userguide/locking/OptimisticLockingTest.java index a7092b80d0..633e73ce5c 100644 --- a/documentation/src/test/java/org/hibernate/userguide/locking/OptimisticLockingTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/locking/OptimisticLockingTest.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.locking; import java.util.Date; diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/Captain.java b/documentation/src/test/java/org/hibernate/userguide/sql/Captain.java new file mode 100644 index 0000000000..aaa2e2a76c --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/sql/Captain.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +//$Id$ +package org.hibernate.userguide.sql; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; + +/** + * @author Emmanuel Bernard + * @author Vlad MIhalcea + */ +//tag::sql-composite-key-entity-associations_named-query-example[] +@Entity +public class Captain { + + @EmbeddedId + private Identity id; + + public Identity getId() { + return id; + } + + public void setId(Identity id) { + this.id = id; + } +} +//end::sql-composite-key-entity-associations_named-query-example[] \ No newline at end of file diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/Dimensions.java b/documentation/src/test/java/org/hibernate/userguide/sql/Dimensions.java new file mode 100644 index 0000000000..30c8f9ff6c --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/sql/Dimensions.java @@ -0,0 +1,40 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +//$Id$ +package org.hibernate.userguide.sql; +import javax.persistence.Embeddable; + +/** + * @author Emmanuel Bernard + * @author Vlad MIhalcea + */ +//tag::sql-composite-key-entity-associations_named-query-example[] +@Embeddable +public class Dimensions { + + private int length; + + private int width; + + public int getLength() { + return length; + } + + public void setLength(int length) { + this.length = length; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } +} +//end::sql-composite-key-entity-associations_named-query-example[] diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/Identity.java b/documentation/src/test/java/org/hibernate/userguide/sql/Identity.java new file mode 100644 index 0000000000..af6162aba2 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/sql/Identity.java @@ -0,0 +1,60 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +//$Id$ +package org.hibernate.userguide.sql; +import java.io.Serializable; +import javax.persistence.Embeddable; + +/** + * @author Emmanuel Bernard + * @author Vlad MIhalcea + */ +//tag::sql-composite-key-entity-associations_named-query-example[] +@Embeddable +public class Identity implements Serializable { + + private String firstname; + + private String lastname; + + public String getFirstname() { + return firstname; + } + + public void setFirstname(String firstname) { + this.firstname = firstname; + } + + public String getLastname() { + return lastname; + } + + public void setLastname(String lastname) { + this.lastname = lastname; + } + + public boolean equals(Object o) { + if ( this == o ) return true; + if ( o == null || getClass() != o.getClass() ) return false; + + final Identity identity = (Identity) o; + + if ( !firstname.equals( identity.firstname ) ) return false; + if ( !lastname.equals( identity.lastname ) ) return false; + + return true; + } + + public int hashCode() { + int result; + result = firstname.hashCode(); + result = 29 * result + lastname.hashCode(); + return result; + } +} +//end::sql-composite-key-entity-associations_named-query-example[] diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/PersonSummaryDTO.java b/documentation/src/test/java/org/hibernate/userguide/sql/PersonSummaryDTO.java new file mode 100644 index 0000000000..bccffd2ae3 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/sql/PersonSummaryDTO.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.userguide.sql; + +import java.math.BigInteger; + +/** + * @author Vlad Mihalcea + */ +//tag::sql-hibernate-dto-query-example[] +public class PersonSummaryDTO { + + private BigInteger id; + + private String name; + + public BigInteger getId() { + return id; + } + + public void setId(BigInteger id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} +//end::sql-hibernate-dto-query-example[] diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/SQLTest.java b/documentation/src/test/java/org/hibernate/userguide/sql/SQLTest.java new file mode 100644 index 0000000000..7ce30f2902 --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/sql/SQLTest.java @@ -0,0 +1,761 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.userguide.sql; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import javax.persistence.PersistenceException; + +import org.hibernate.Criteria; +import org.hibernate.Session; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; +import org.hibernate.loader.custom.NonUniqueDiscoveredSqlAliasException; +import org.hibernate.transform.Transformers; +import org.hibernate.type.LongType; +import org.hibernate.type.StringType; +import org.hibernate.userguide.model.AddressType; +import org.hibernate.userguide.model.Call; +import org.hibernate.userguide.model.CreditCardPayment; +import org.hibernate.userguide.model.Partner; +import org.hibernate.userguide.model.Person; +import org.hibernate.userguide.model.PersonNames; +import org.hibernate.userguide.model.Phone; +import org.hibernate.userguide.model.PhoneType; +import org.hibernate.userguide.model.WireTransferPayment; + +import org.junit.Before; +import org.junit.Test; + +import org.jboss.logging.Logger; + +import static org.hibernate.userguide.util.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +/** + * @author Vlad Mihalcea + */ +public class SQLTest extends BaseEntityManagerFunctionalTestCase { + + private static final Logger log = Logger.getLogger( SQLTest.class ); + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class, + Partner.class, + Phone.class, + Call.class, + CreditCardPayment.class, + WireTransferPayment.class, + SpaceShip.class, + Captain.class, + }; + } + + @Before + public void init() { + doInJPA( this::entityManagerFactory, entityManager -> { + 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.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 ) )) ; + entityManager.persist(person2); + + Person person3 = new Person("Dr_ John Doe" ); + entityManager.persist(person3); + + Phone phone1 = new Phone( "123-456-7890" ); + 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 ) ) ); + + Call call11 = new Call(); + call11.setDuration( 12 ); + call11.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 0, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + + Call call12 = new Call(); + call12.setDuration( 33 ); + call12.setTimestamp( Timestamp.from( LocalDateTime.of( 2000, 1, 1, 1, 0, 0 ).toInstant( ZoneOffset.UTC ) ) ); + + phone1.addCall(call11); + phone1.addCall(call12); + + Phone phone2 = new Phone( "098_765-4321" ); + phone2.setId( 2L ); + phone2.setType( PhoneType.LAND_LINE ); + + Phone phone3 = new Phone( "098-765-4320" ); + phone3.setId( 3L ); + phone3.setType( PhoneType.LAND_LINE ); + + person2.addPhone( phone2 ); + person2.addPhone( phone3 ); + + CreditCardPayment creditCardPayment = new CreditCardPayment(); + creditCardPayment.setCompleted( true ); + creditCardPayment.setAmount( BigDecimal.ZERO ); + creditCardPayment.setPerson( person1 ); + + WireTransferPayment wireTransferPayment = new WireTransferPayment(); + wireTransferPayment.setCompleted( true ); + wireTransferPayment.setAmount( BigDecimal.valueOf( 100 ) ); + wireTransferPayment.setPerson( person2 ); + + entityManager.persist( creditCardPayment ); + entityManager.persist( wireTransferPayment ); + + Partner partner = new Partner( "John Doe" ); + entityManager.persist( partner ); + + Captain captain = new Captain(); + captain.setId( new Identity() ); + captain.getId().setFirstname( "Jean-Luc" ); + captain.getId().setLastname( "Picard" ); + + entityManager.persist( captain ); + + SpaceShip spaceShip = new SpaceShip(); + spaceShip.setName( "Enterprise" ); + spaceShip.setDimensions( new Dimensions() ); + spaceShip.getDimensions().setLength( 100 ); + spaceShip.getDimensions().setWidth( 20 ); + spaceShip.setModel( "E-1" ); + spaceShip.setSpeed( 150 ); + spaceShip.setCaptain( captain ); + entityManager.persist( spaceShip ); + }); + } + + @Test + public void test_sql_jpa_all_columns_scalar_query_example() { + + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-all-columns-scalar-query-example[] + List persons = entityManager.createNativeQuery( + "SELECT * FROM person" ) + .getResultList(); + //end::sql-jpa-all-columns-scalar-query-example[] + assertEquals( 3, persons.size() ); + } ); + } + + @Test + public void test_sql_jpa_custom_column_selection_scalar_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-custom-column-selection-scalar-query-example[] + List persons = entityManager.createNativeQuery( + "SELECT id, name FROM person" ) + .getResultList(); + + for(Object[] person : persons) { + BigInteger id = (BigInteger) person[0]; + String name = (String) person[1]; + } + //end::sql-jpa-custom-column-selection-scalar-query-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_hibernate_query_scalar_example() { + + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-all-columns-scalar-query-example[] + List persons = session.createSQLQuery( + "SELECT * FROM person" ) + .list(); + //end::sql-hibernate-all-columns-scalar-query-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_hibernate_custom_column_selection_scalar_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-custom-column-selection-scalar-query-example[] + List persons = session.createSQLQuery( + "SELECT id, name FROM person" ) + .list(); + + for(Object[] person : persons) { + BigInteger id = (BigInteger) person[0]; + String name = (String) person[1]; + } + //end::sql-hibernate-custom-column-selection-scalar-query-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_hibernate_query_scalar_explicit_result_set_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-scalar-query-explicit-result-set-example[] + List persons = session.createSQLQuery( + "SELECT * FROM person" ) + .addScalar( "id", LongType.INSTANCE ) + .addScalar( "name", StringType.INSTANCE ) + .list(); + + for(Object[] person : persons) { + Long id = (Long) person[0]; + String name = (String) person[1]; + } + //end::sql-hibernate-scalar-query-explicit-result-set-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_hibernate_query_scalar_partial_explicit_result_set_example() { + + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-scalar-query-partial-explicit-result-set-example[] + List persons = session.createSQLQuery( + "SELECT * FROM person" ) + .addScalar( "id", LongType.INSTANCE ) + .addScalar( "name" ) + .list(); + + for(Object[] person : persons) { + Long id = (Long) person[0]; + String name = (String) person[1]; + } + //end::sql-hibernate-scalar-query-partial-explicit-result-set-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_jpa_entity_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-entity-query-example[] + List persons = entityManager.createNativeQuery( + "SELECT * FROM person", Person.class ) + .getResultList(); + //end::sql-jpa-entity-query-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_hibernate_entity_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-entity-query-example[] + List persons = session.createSQLQuery( + "SELECT * FROM person" ) + .addEntity( Person.class ) + .list(); + //end::sql-hibernate-entity-query-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_jpa_entity_query_explicit_result_set_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-entity-query-explicit-result-set-example[] + List persons = entityManager.createNativeQuery( + "SELECT id, name, nickName, address, createdOn, version " + + "FROM person", Person.class ) + .getResultList(); + //end::sql-jpa-entity-query-explicit-result-set-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_hibernate_entity_query_explicit_result_set_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-entity-query-explicit-result-set-example[] + List persons = session.createSQLQuery( + "SELECT id, name, nickName, address, createdOn, version " + + "FROM person" ) + .addEntity( Person.class ) + .list(); + //end::sql-hibernate-entity-query-explicit-result-set-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_jpa_entity_associations_query_many_to_one_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-entity-associations-query-many-to-one-example[] + List phones = entityManager.createNativeQuery( + "SELECT id, number, type, person_id " + + "FROM phone", Phone.class ) + .getResultList(); + //end::sql-jpa-entity-associations-query-many-to-one-example[] + assertEquals(3, phones.size()); + }); + } + + @Test + public void test_sql_hibernate_entity_associations_query_many_to_one_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-entity-associations-query-many-to-one-example[] + List phones = session.createSQLQuery( + "SELECT id, number, type, person_id " + + "FROM phone" ) + .addEntity( Phone.class ) + .list(); + //end::sql-hibernate-entity-associations-query-many-to-one-example[] + assertEquals(3, phones.size()); + }); + } + + @Test + public void test_sql_jpa_entity_associations_query_many_to_one_join_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-entity-associations-query-many-to-one-join-example[] + List phones = entityManager.createNativeQuery( + "SELECT * " + + "FROM phone ph " + + "JOIN person pr ON ph.person_id = pr.id", Phone.class ) + .getResultList(); + + for(Phone phone : phones) { + Person person = phone.getPerson(); + } + //end::sql-jpa-entity-associations-query-many-to-one-join-example[] + assertEquals(3, phones.size()); + }); + } + + @Test + public void test_sql_hibernate_entity_associations_query_many_to_one_join_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-entity-associations-query-many-to-one-join-example[] + List tuples = session.createSQLQuery( + "SELECT * " + + "FROM phone ph " + + "JOIN person pr ON ph.person_id = pr.id" ) + .addEntity("phone", Phone.class ) + .addJoin( "pr", "phone.person") + .list(); + + for(Object[] tuple : tuples) { + Phone phone = (Phone) tuple[0]; + Person person = (Person) tuple[1]; + } + //end::sql-hibernate-entity-associations-query-many-to-one-join-example[] + assertEquals(3, tuples.size()); + }); + } + + @Test + public void test_sql_hibernate_entity_associations_query_many_to_one_join_result_transformer_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-entity-associations-query-many-to-one-join-result-transformer-example[] + List persons = session.createSQLQuery( + "SELECT * " + + "FROM phone ph " + + "JOIN person pr ON ph.person_id = pr.id" ) + .addEntity("phone", Phone.class ) + .addJoin( "pr", "phone.person") + .setResultTransformer( Criteria.ROOT_ENTITY ) + .list(); + + for(Person person : persons) { + person.getPhones(); + } + //end::sql-hibernate-entity-associations-query-many-to-one-join-result-transformer-example[] + assertEquals(3, persons.size()); + }); + } + + @Test + public void test_sql_jpa_entity_associations_query_one_to_many_join_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-entity-associations-query-one-to-many-join-example[] + List phones = entityManager.createNativeQuery( + "SELECT * " + + "FROM phone ph " + + "JOIN call c ON c.phone_id = ph.id", Phone.class ) + .getResultList(); + + for(Phone phone : phones) { + List calls = phone.getCalls(); + } + //end::sql-jpa-entity-associations-query-one-to-many-join-example[] + assertEquals(2, phones.size()); + }); + } + + @Test + public void test_sql_hibernate_entity_associations_query_one_to_many_join_example_1() { + try { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + List phones = session.createSQLQuery( + "SELECT * " + + "FROM phone ph " + + "JOIN call c ON c.phone_id = ph.id" ) + .addEntity("phone", Phone.class ) + .addJoin( "c", "phone.calls") + .setResultTransformer( Criteria.DISTINCT_ROOT_ENTITY ) + .list(); + + for(Phone phone : phones) { + List calls = phone.getCalls(); + } + assertEquals(2, phones.size()); + }); + } + catch (Exception e) { + log.error( "HHH-10504", e ); + //See issue https://hibernate.atlassian.net/browse/HHH-10504 + } + } + + @Test + public void test_sql_hibernate_entity_associations_query_one_to_many_join_example_2() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-entity-associations-query-one-to-many-join-example[] + List tuples = session.createSQLQuery( + "SELECT * " + + "FROM phone ph " + + "JOIN call c ON c.phone_id = ph.id" ) + .addEntity("phone", Phone.class ) + .addJoin( "c", "phone.calls") + .list(); + + for(Object[] tuple : tuples) { + Phone phone = (Phone) tuple[0]; + Call call = (Call) tuple[1]; + } + //end::sql-hibernate-entity-associations-query-one-to-many-join-example[] + assertEquals(2, tuples.size()); + }); + } + + @Test + public void test_sql_jpa_multi_entity_query_example() { + try { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-multi-entity-query-example[] + List entities = entityManager.createNativeQuery( + "SELECT * " + + "FROM person pr, partner pt " + + "WHERE pr.name = pt.name" ) + .getResultList(); + //end::sql-jpa-multi-entity-query-example[] + assertEquals(2, entities.size()); + }); + fail("Should throw NonUniqueDiscoveredSqlAliasException!"); + } + catch (PersistenceException expected) { + assertEquals( NonUniqueDiscoveredSqlAliasException.class, expected.getCause().getClass() ); + } + } + + @Test(expected = NonUniqueDiscoveredSqlAliasException.class) + public void test_sql_hibernate_multi_entity_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-multi-entity-query-example[] + List entities = session.createSQLQuery( + "SELECT * " + + "FROM person pr, partner pt " + + "WHERE pr.name = pt.name" ) + .list(); + //end::sql-hibernate-multi-entity-query-example[] + assertEquals(2, entities.size()); + }); + fail("Should throw NonUniqueDiscoveredSqlAliasException!"); + } + + @Test + public void test_sql_hibernate_multi_entity_query_alias_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-multi-entity-query-alias-example[] + List entities = session.createSQLQuery( + "SELECT {pr.*}, {pt.*} " + + "FROM person pr, partner pt " + + "WHERE pr.name = pt.name" ) + .addEntity( "pr", Person.class) + .addEntity( "pt", Partner.class) + .list(); + //end::sql-hibernate-multi-entity-query-alias-example[] + assertEquals(1, entities.size()); + }); + } + + @Test + public void test_sql_hibernate_dto_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-dto-query-example[] + List dtos = session.createSQLQuery( + "SELECT p.id as \"id\", p.name as \"name\" " + + "FROM person p") + .setResultTransformer( Transformers.aliasToBean( PersonSummaryDTO.class ) ) + .list(); + //end::sql-hibernate-dto-query-example[] + assertEquals(3, dtos.size()); + }); + } + + @Test + public void test_sql_hibernate_inheritance_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-inheritance-query-example[] + List payments = session.createSQLQuery( + "SELECT * " + + "FROM Payment p " + + "JOIN CreditCardPayment cp on cp.id = p.id" ) + .addEntity( CreditCardPayment.class ) + .list(); + //end::sql-hibernate-inheritance-query-example[] + assertEquals(1, payments.size()); + }); + } + + @Test + public void test_sql_jpa_query_parameters_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-query-parameters-example[] + List persons = entityManager.createNativeQuery( + "SELECT * " + + "FROM person " + + "WHERE name like :name", Person.class ) + .setParameter("name", "J%") + .getResultList(); + //end::sql-jpa-query-parameters-example[] + assertEquals(1, persons.size()); + }); + } + + @Test + public void test_sql_hibernate_query_parameters_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-query-parameters-example[] + List persons = session.createSQLQuery( + "SELECT * " + + "FROM person " + + "WHERE name like :name" ) + .addEntity( Person.class ) + .setParameter("name", "J%") + .list(); + //end::sql-hibernate-query-parameters-example[] + assertEquals(1, persons.size()); + }); + } + + @Test + public void test_sql_jpa_scalar_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-scalar-named-query-example[] + List names = entityManager.createNamedQuery( + "find_person_name" ) + .getResultList(); + //end::sql-jpa-scalar-named-query-example[] + assertEquals(3, names.size()); + }); + } + + @Test + public void test_sql_hibernate_scalar_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-scalar-named-query-example[] + List names = session.getNamedQuery( + "find_person_name" ) + .list(); + //end::sql-hibernate-scalar-named-query-example[] + assertEquals(3, names.size()); + }); + } + + @Test + public void test_sql_jpa_multiple_scalar_values_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-multiple-scalar-values-named-query-example[] + List tuples = entityManager.createNamedQuery( + "find_person_name_and_nickName" ) + .getResultList(); + + for(Object[] tuple : tuples) { + String name = (String) tuple[0]; + String nickName = (String) tuple[1]; + } + //end::sql-jpa-multiple-scalar-values-named-query-example[] + assertEquals(3, tuples.size()); + }); + } + + @Test + public void test_sql_hibernate_multiple_scalar_values_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-multiple-scalar-values-named-query-example[] + List tuples = session.getNamedQuery( + "find_person_name_and_nickName" ) + .list(); + + for(Object[] tuple : tuples) { + String name = (String) tuple[0]; + String nickName = (String) tuple[1]; + } + //end::sql-hibernate-multiple-scalar-values-named-query-example[] + assertEquals(3, tuples.size()); + }); + } + + @Test + public void test_sql_jpa_multiple_scalar_values_dto_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-multiple-scalar-values-dto-named-query-example[] + List personNames = entityManager.createNamedQuery( + "find_person_name_and_nickName_dto" ) + .getResultList(); + //end::sql-jpa-multiple-scalar-values-dto-named-query-example[] + assertEquals(3, personNames.size()); + }); + } + + @Test + public void test_sql_hibernate_multiple_scalar_values_dto_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-multiple-scalar-values-dto-named-query-example[] + List personNames = session.getNamedQuery( + "find_person_name_and_nickName_dto" ) + .list(); + //end::sql-hibernate-multiple-scalar-values-dto-named-query-example[] + assertEquals(3, personNames.size()); + }); + } + + @Test + public void test_sql_jpa_entity_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-entity-named-query-example[] + List persons = entityManager.createNamedQuery( + "find_person_by_name" ) + .setParameter("name", "J%") + .getResultList(); + //end::sql-jpa-entity-named-query-example[] + assertEquals(1, persons.size()); + }); + } + + @Test + public void test_sql_hibernate_entity_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-entity-named-query-example[] + List persons = session.getNamedQuery( + "find_person_by_name" ) + .setParameter("name", "J%") + .list(); + //end::sql-hibernate-entity-named-query-example[] + assertEquals(1, persons.size()); + }); + } + + @Test + public void test_sql_jpa_entity_associations_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-entity-associations_named-query-example[] + List tuples = entityManager.createNamedQuery( + "find_person_with_phones_by_name" ) + .setParameter("name", "J%") + .getResultList(); + + for(Object[] tuple : tuples) { + Person person = (Person) tuple[0]; + Phone phone = (Phone) tuple[1]; + } + //end::sql-jpa-entity-associations_named-query-example[] + assertEquals(1, tuples.size()); + }); + } + + @Test + public void test_sql_hibernate_entity_associations_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-entity-associations_named-query-example[] + List tuples = session.getNamedQuery( + "find_person_with_phones_by_name" ) + .setParameter("name", "J%") + .list(); + + for(Object[] tuple : tuples) { + Person person = (Person) tuple[0]; + Phone phone = (Phone) tuple[1]; + } + //end::sql-hibernate-entity-associations_named-query-example[] + assertEquals(1, tuples.size()); + }); + } + + @Test + public void test_sql_jpa_composite_key_entity_associations_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + //tag::sql-jpa-composite-key-entity-associations_named-query-example[] + List tuples = entityManager.createNamedQuery( + "find_all_spaceships" ) + .getResultList(); + + for(Object[] tuple : tuples) { + SpaceShip spaceShip = (SpaceShip) tuple[0]; + Integer surface = (Integer) tuple[1]; + Integer volume = (Integer) tuple[2]; + } + //end::sql-jpa-composite-key-entity-associations_named-query-example[] + assertEquals(1, tuples.size()); + }); + } + + @Test + public void test_sql_hibernate_composite_key_entity_associations_named_query_example() { + doInJPA( this::entityManagerFactory, entityManager -> { + Session session = entityManager.unwrap( Session.class ); + //tag::sql-hibernate-composite-key-entity-associations_named-query-example[] + List tuples = session.getNamedQuery( + "find_all_spaceships" ) + .list(); + + for(Object[] tuple : tuples) { + SpaceShip spaceShip = (SpaceShip) tuple[0]; + Integer surface = (Integer) tuple[1]; + Integer volume = (Integer) tuple[2]; + } + //end::sql-hibernate-composite-key-entity-associations_named-query-example[] + assertEquals(1, tuples.size()); + }); + } +} diff --git a/documentation/src/test/java/org/hibernate/userguide/sql/SpaceShip.java b/documentation/src/test/java/org/hibernate/userguide/sql/SpaceShip.java new file mode 100644 index 0000000000..9fca478e1c --- /dev/null +++ b/documentation/src/test/java/org/hibernate/userguide/sql/SpaceShip.java @@ -0,0 +1,124 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +//$Id$ +package org.hibernate.userguide.sql; + +import javax.persistence.ColumnResult; +import javax.persistence.Entity; +import javax.persistence.EntityResult; +import javax.persistence.FetchType; +import javax.persistence.FieldResult; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.ManyToOne; +import javax.persistence.NamedNativeQueries; +import javax.persistence.NamedNativeQuery; +import javax.persistence.SqlResultSetMapping; + +/** + * @author Emmanuel Bernard + * @author Vlad MIhalcea + */ +//tag::sql-composite-key-entity-associations_named-query-example[] +@Entity +@NamedNativeQueries({ + @NamedNativeQuery(name = "find_all_spaceships", + query = + "SELECT " + + " name as \"name\", " + + " model, " + + " speed, " + + " lname as lastn, " + + " fname as firstn, " + + " length, " + + " width, " + + " length * width as surface, " + + " length * width * 10 as volume " + + "FROM SpaceShip", + resultSetMapping = "spaceship" + ) +}) +@SqlResultSetMapping( + name = "spaceship", + entities = @EntityResult( + entityClass = SpaceShip.class, + fields = { + @FieldResult(name = "name", column = "name"), + @FieldResult(name = "model", column = "model"), + @FieldResult(name = "speed", column = "speed"), + @FieldResult(name = "captain.lastname", column = "lastn"), + @FieldResult(name = "captain.firstname", column = "firstn"), + @FieldResult(name = "dimensions.length", column = "length"), + @FieldResult(name = "dimensions.width", column = "width"), + } + ), + columns = { + @ColumnResult(name = "surface"), + @ColumnResult(name = "volume") + } +) +public class SpaceShip { + + @Id + private String name; + + private String model; + + private double speed; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumns({ + @JoinColumn(name = "fname", referencedColumnName = "firstname"), + @JoinColumn(name = "lname", referencedColumnName = "lastname") + }) + private Captain captain; + + private Dimensions dimensions; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public double getSpeed() { + return speed; + } + + public void setSpeed(double speed) { + this.speed = speed; + } + + public Captain getCaptain() { + return captain; + } + + public void setCaptain(Captain captain) { + this.captain = captain; + } + + public Dimensions getDimensions() { + return dimensions; + } + + public void setDimensions(Dimensions dimensions) { + this.dimensions = dimensions; + } +} +//end::sql-composite-key-entity-associations_named-query-example[] diff --git a/documentation/src/test/java/org/hibernate/userguide/transactions/TransactionsTest.java b/documentation/src/test/java/org/hibernate/userguide/transactions/TransactionsTest.java index 8b749527c5..f564d26fbc 100644 --- a/documentation/src/test/java/org/hibernate/userguide/transactions/TransactionsTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/transactions/TransactionsTest.java @@ -1,3 +1,9 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ package org.hibernate.userguide.transactions; import javax.persistence.Entity; diff --git a/documentation/src/test/resources/log4j.properties b/documentation/src/test/resources/log4j.properties index 8c08221a00..e2f824168b 100644 --- a/documentation/src/test/resources/log4j.properties +++ b/documentation/src/test/resources/log4j.properties @@ -25,8 +25,7 @@ log4j.logger.org.hibernate.reflection=info log4j.logger.org.hibernate.SQL=debug ### log JDBC bind parameters ### -#log4j.logger.org.hibernate.type=info -log4j.logger.org.hibernate.type=trace +log4j.logger.org.hibernate.type=info ### log schema export/update ### log4j.logger.org.hibernate.tool.hbm2ddl=info