add an examples with treat() (#4552)

and slightly reorg doc
This commit is contained in:
Gavin King 2022-01-02 15:45:48 +01:00 committed by GitHub
parent 5ae55d7bfb
commit c48be75d3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 203 additions and 32 deletions

View File

@ -419,7 +419,7 @@ HQL allows Java `static` constants to be used in the same way, for example, `jav
[[hql-entity-name-literals]]
==== Literal entity names
Entity names may also occur as a literal value. They do not need to be qualified. See <<hql-entity-type-exp>>.
Entity names may also occur as a literal value. They do not need to be qualified. See <<hql-treat-type>>.
[[hql-expressions]]
=== Expressions
@ -500,31 +500,13 @@ Please carefully note the difference between these two operations: `by` and `ext
Additional datetime operations, including the useful `format()` function, are defined below, in <<hql-exp-functions>>.
[[hql-identification-variable]]
==== Identification variable
[[hql-path-expressions]]
==== Identification variables and path expressions
Identification variables, and path expressions beginning with an identification variable are legal expression in almost every context.
See <<hql-from-clause>>.
[[hql-path-expressions]]
==== Path expressions
Again, see <<hql-from-clause>>.
[[hql-entity-type-exp]]
==== Entity type
The special function `type()`, applied to an identification variable, evaluates to the entity name of the referenced entity.
This is mainly useful when dealing with entity inheritance hierarchies.
[[hql-entity-type-exp-example]]
//.Entity type expression examples
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-entity-type-exp-example]
----
====
[[hql-case-expressions]]
==== Case expressions
@ -602,9 +584,6 @@ include::{sourcedir}/HQLTest.java[tags=hql-case-arithmetic-expressions-example]
If the arithmetic expression was not enclosed in parentheses, the parser would be unable to parse the expression.
====
==== Typecasts
The `treat` operator may be used to downcast the type of an identification variable, for example, `treat(payment as CreditCardPayment)`.
[[hql-exp-functions]]
=== Functions
@ -613,6 +592,37 @@ Both HQL and JPQL define some standard functions that are portable between datab
In addition, there are several ways to use a database function that's not known to Hibernate.
[[hql-treat-type]]
==== Types and typecasts
The special function `type()`, applied to an identification variable, evaluates to the entity name of the referenced entity.
This is mainly useful when dealing with entity inheritance hierarchies.
[[hql-entity-type-exp-example]]
//.Entity type expression examples
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-entity-type-exp-example]
----
====
The special function `treat()` may be used to narrow the type of an identification variable.
This is useful when dealing with entity inheritance hierarchies.
[[hql-treat-example]]
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-treat-example]
----
====
The type of the expression `treat(p as CreditCardPayment)` is the narrowed type, `CreditCardPayment`, instead of the declared type `Payment` of `p`.
This allows the attribute `cardNumber` declared by the subtype `CreditCardPayment` to be referenced.
The `treat()` function may even occur in a <<hql-join-treat,join>>.
[[jpql-standardized-functions]]
==== JPQL standard functions
@ -1102,7 +1112,7 @@ The type of the expression on the left, and the types of all the values on the r
JPQL limits the legal types to string, numeric, date/time, and enum types, and in JPQL the left expression must be either:
* a "state field", which means a simple attribute, excluding associations and embedded attributes, or
* an entity type expression (see <<hql-entity-type-exp>>).
* an entity type expression (see <<hql-treat-type>>).
HQL is far more permissive. HQL itself does not restrict the type any way, though the database itself might.
Even embedded attributes are allowed, although that feature depends on the level of support for tuple or "row value constructors" in the underlying database.
@ -1445,10 +1455,23 @@ Fetch joins should not be used in paged queries (`setFirstResult()` or `setMaxRe
Nor should they be used with the `scroll()` or `stream()` methods.
====
[[hql-treat-as]]
==== Joins with `treat`
[[hql-join-treat]]
==== Joins with typecasts
A join may narrow the type of the joined entity using `treat()`, for example, `join treat(a.payments as CreditCardPayment)`.
An explicit join may narrow the type of the joined entity using `treat()`.
[[hql-join-treat-example]]
====
[source, JAVA, indent=0]
----
include::{sourcedir}/HQLTest.java[tags=hql-join-treat-example]
----
====
Here, the identification variable `ccp` declared to the right of `treat()` has the narrowed type `CreditCardPayment`, instead of the declared type `Payment`.
This allows the attribute `cardNumber` declared by the subtype `CreditCardPayment` to be referenced in the rest of the query.
See <<hql-treat-type>> for more information about `treat()`.
[[hql-implicit-join]]
==== Implicit joins (path expressions)

View File

@ -0,0 +1,41 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.userguide.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Account {
@Id
@GeneratedValue
long id;
@ManyToOne
Person owner;
@OneToMany(mappedBy = "account")
List<Payment> payments = new ArrayList<>();
public List<Payment> getPayments() {
return payments;
}
public Person getOwner() {
return owner;
}
public void setOwner(Person owner) {
this.owner = owner;
}
}

View File

@ -35,6 +35,9 @@ public class Call {
private int duration;
@ManyToOne
private Payment payment;
//Getters and setters are omitted for brevity
//end::hql-examples-domain-model-example[]
@ -71,6 +74,14 @@ public class Call {
public void setDuration(int duration) {
this.duration = duration;
}
//tag::hql-examples-domain-model-example[]
public Payment getPayment() {
return payment;
}
public void setPayment(Payment payment) {
this.payment = payment;
}
//tag::hql-examples-domain-model-example[]
}
//end::hql-examples-domain-model-example[]

View File

@ -14,5 +14,14 @@ import jakarta.persistence.Entity;
//tag::hql-examples-domain-model-example[]
@Entity
public class CreditCardPayment extends Payment {
String cardNumber;
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
public String getCardNumber() {
return cardNumber;
}
}
//end::hql-examples-domain-model-example[]

View File

@ -30,6 +30,9 @@ public class Payment {
private boolean completed;
@ManyToOne
private Account account;
@ManyToOne
private Person person;
@ -68,6 +71,14 @@ public class Payment {
public void setPerson(Person person) {
this.person = person;
}
//tag::hql-examples-domain-model-example[]
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
//tag::hql-examples-domain-model-example[]
}
//end::hql-examples-domain-model-example[]

View File

@ -18,8 +18,10 @@ import org.hibernate.StatelessSession;
import org.hibernate.Transaction;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.resource.transaction.spi.TransactionStatus;
import org.hibernate.userguide.model.Account;
import org.hibernate.userguide.model.Call;
import org.hibernate.userguide.model.Partner;
import org.hibernate.userguide.model.Payment;
import org.hibernate.userguide.model.Person;
import org.hibernate.userguide.model.Phone;
@ -39,6 +41,8 @@ public class BatchTest extends BaseEntityManagerFunctionalTestCase {
Person.class,
Phone.class,
Call.class,
Account.class,
Payment.class,
Partner.class
};
}

View File

@ -23,6 +23,7 @@ import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.Root;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.userguide.model.Account;
import org.hibernate.userguide.model.AddressType;
import org.hibernate.userguide.model.Call;
import org.hibernate.userguide.model.CreditCardPayment;
@ -55,6 +56,7 @@ public class CriteriaTest extends BaseEntityManagerFunctionalTestCase {
Call.class,
CreditCardPayment.class,
WireTransferPayment.class,
Account.class,
Event.class
};
}

View File

@ -33,7 +33,9 @@ import org.hibernate.dialect.SQLServerDialect;
import org.hibernate.dialect.TiDBDialect;
import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase;
import org.hibernate.query.QueryProducer;
import org.hibernate.testing.Skip;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.userguide.model.Account;
import org.hibernate.userguide.model.AddressType;
import org.hibernate.userguide.model.Call;
import org.hibernate.userguide.model.CreditCardPayment;
@ -67,6 +69,7 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
Person.class,
Phone.class,
Call.class,
Account.class,
CreditCardPayment.class,
WireTransferPayment.class
};
@ -120,15 +123,28 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
person2.addPhone( phone2 );
person2.addPhone( phone3 );
Account account1 = new Account();
account1.setOwner( person1 );
entityManager.persist( account1 );
Account account2 = new Account();
account1.setOwner( person2 );
entityManager.persist( account2 );
CreditCardPayment creditCardPayment = new CreditCardPayment();
creditCardPayment.setCompleted( true );
creditCardPayment.setAmount( BigDecimal.ZERO );
creditCardPayment.setPerson( person1 );
creditCardPayment.setCardNumber("1234-1234-1234-1234");
creditCardPayment.setAccount( account1 );
call11.setPayment( creditCardPayment );
WireTransferPayment wireTransferPayment = new WireTransferPayment();
wireTransferPayment.setCompleted( true );
wireTransferPayment.setAmount( BigDecimal.valueOf( 100 ) );
wireTransferPayment.setPerson( person2 );
wireTransferPayment.setAccount( account2 );
call12.setPayment( wireTransferPayment );
entityManager.persist( creditCardPayment );
entityManager.persist( wireTransferPayment );
@ -1886,6 +1902,56 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
});
}
@Test
public void test_hql_treat_example() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::hql-treat-example[]
List<Payment> payments = entityManager.createQuery(
"select p " +
"from Payment p " +
"where length(treat(p as CreditCardPayment).cardNumber) between 16 and 20",
Payment.class )
.getResultList();
//end::hql-treat-example[]
assertEquals(1, payments.size());
});
}
@Test @Skip(condition = Skip.AlwaysSkip.class, message = "broken in H6")
public void test_hql_join_many_treat_example() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::hql-join-treat-example[]
// a to-many association
List<Object[]> payments = entityManager.createQuery(
"select a, ccp " +
"from Account a " +
"join treat(a.payments as CreditCardPayment) ccp " +
"where length(ccp.cardNumber) between 16 and 20",
Object[].class )
.getResultList();
//end::hql-join-treat-example[]
assertEquals(1, payments.size());
});
}
@Test
public void test_hql_join_one_treat_example() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::hql-join-treat-example[]
// a to-one association
List<Object[]> payments = entityManager.createQuery(
"select c, ccp " +
"from Call c " +
"join treat(c.payment as CreditCardPayment) ccp " +
"where length(ccp.cardNumber) between 16 and 20",
Object[].class )
.getResultList();
//end::hql-join-treat-example[]
assertEquals(1, payments.size());
});
}
@Test
public void test_hql_entity_type_exp_example_1() {
doInJPA( this::entityManagerFactory, entityManager -> {
@ -1905,6 +1971,8 @@ public class HQLTest extends BaseEntityManagerFunctionalTestCase {
public void test_hql_entity_type_exp_example_2() {
doInJPA( this::entityManagerFactory, entityManager -> {
//tag::hql-entity-type-exp-example[]
// using a parameter instead of a literal entity type
List<Payment> payments = entityManager.createQuery(
"select p " +
"from Payment p " +

View File

@ -20,6 +20,7 @@ import org.hibernate.loader.NonUniqueDiscoveredSqlAliasException;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.transform.Transformers;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.userguide.model.Account;
import org.hibernate.userguide.model.AddressType;
import org.hibernate.userguide.model.Call;
import org.hibernate.userguide.model.CreditCardPayment;
@ -55,6 +56,7 @@ public class SQLTest extends BaseEntityManagerFunctionalTestCase {
Partner.class,
Phone.class,
Call.class,
Account.class,
CreditCardPayment.class,
WireTransferPayment.class,
SpaceShip.class,