SQM defines multiple "layers" to its type system. The lowest level is called SqmDomainType. The higher-level
concept is called SqmExpressableType (which is a DomainTypeExporter which means it exposes a SqmDomainType).
The SqmDomainType is more-or-less akin to the Java type - on the ORM side, it actually relates to the
org.hibernate.type.spi.Type contract. SqmDomainType is further categorized into:
-
SqmDomainTypeBasic- a basic value -
SqmDomainTypeEmbeddable- an embeddable/composite value -
SqmDomainTypeEntity- an entity value -
SqmDomainTypeAny- not yet implemented
A SqmExpressableType on the other hand represents a specific "thing" that can be the basis for the type
of an SqmExpression (see below). SqmExpressableType is further categorized into:
-
SqmExpressableTypeBasic -
SqmNavigable-
SqmNavigableSource
-
SqmExpressableTypeEntity -
SqmExpressableTypeEmbedded -
SqmPluralAttribute
-
-
SqmAttribute -
SqmEntityIdentifier -
SqmPluralAttributeIndex -
SqmPluralAttributeElement -
SqmExpressableTypeAny- not yet implemented
-
On the ORM side it is expected that SqmExpressableType forms the root of the JPA type system impl.
The SqmNavigable aspect is hugely important to understand. It essentially equates to what JPA
calls a "Bindable". It defines parts of the domain model that are eligible to be navigated as part of
path expressions. A SqmNavigable exposes it’s "source" as a SqmNavigableSource. As an example, the
SqmNavigable for an entity attribute Person#age would the the "age" part and its source would be the
EntityPersister for Person.
A SqmNavigableSource is always a SqmNavigable. A SqmNavigable may or may not be a SqmNavigableSource.
The important concept here is that a SqmNavigable is a SqmExpressableType, meaning that the navigable itself
can be used to describe the "type" of an expression, which is important as we start to consolidate ORM-metadata
into these navigable domain descriptors on the ORM side (e.g. AttributeConverter on the SingularAttribute rather
than the Attribute’s Type).
Expressions (SqmExpression) are the basic building block of the query model - they come in many different
varieties, e.g.
* a literal
* a parameter to a reference
* a reference to a SqmNavigable
* an arithmetic operation
* a function call
* etc
Even a predicate is an expression! See section on predicates below…
SqmExpression mainly exposes information about the "type" of the expressions in terms of the
SqmExpressableType discussed above. Specifically:
-
SqmExpression#getExpressionType- the type of this expression, if known -
SqmExpression#getInferableType- the type, if known, that can be used to determine the type of otherSqmExpressionnodes in
#getExpressionType returns the type of this SqmExpression, if known. #getInferableType
returns the type, if known, that this SqmExpression "exports" for the purpose of determining
"related" SqmExpression nodes that did not explicitly define a type. E.g., assuming a query like:
select p from Person p where p.age = ?1
the predicate defines 2 SqmExpression nodes:
-
the
SqmExpressionfor the navigablep.age -
the
SqmExpressionfor the parameter?1.
?1 has no inherent type. However, because it is involved in an equality predicate, we can look at
the "other side" of the predicate to determine an "implicit" type for the parameter node. Specifically
we can look at the other side’s #getInferableType. Here, we inherently know the type of p.age because
Person#age is a mapped attribute. So we’d expect the parameter to be of the same type.
The SqmExpression form of a SqmNavigable is called a SqmNavigableBinding. The SqmExpression form of
a SqmNavigableSource is called a SqmNavigableSourceBinding. Just like a SqmNavigable exposes its
SqmNavigableSource, so too a SqmNavigableBinding exposes its SqmNavigableSourceBinding.
Let’s look at some example queries and how they translate into expressions, navigable and bindings…
select p from Person p
As we saw above, the reference Person p creates a SqmRoot element. SqmFrom elements do not themselves
implement SqmExpression. However, SqmFrom elements may be expressed via their SqmFrom#getBinding returning
a SqmNavigableBinding. Here specifically the SqmRoot(Person,p)#getBinding call returns the
EntityBindingImpl that represents the SqmRoot(Person,p)#getBinding in places where an expression is needed, such
as the reference to p as a selection.
select p, pm from Person p, Person pm
Here we end up with 2 distinct SqmFrom elements (SqmRoot(Person,p) and SqmRoot(Person,pm)) each of which
also defines its own EntityBindingImpl (SqmNavigableBining) reference. The selection expressions actually refer
to those EntityBindingImpl references.
select p.age + 10 from Person p
What is the type of the p.age + 10 expression? The answer is… it depends. Specifically, it depends on
the type of p.age. 10 is interpreted as a literal integer. The type of the arithmetic expression overall
then is the resolution of the type you get when you add together (1) a value of the same type as p.age and
(2) a value of integer type.
This is where the SqmExpressableType contract mentioned above comes into play. Both p.age and 10
expose their SqmExpressableType:
. p.age - its SqmExpressableType would depend on the exact mapping, but let’s assume the persistent
attribute is defined as an Integer - which means its SqmExpressableType would be
a OrmSingularAttributeBasic (as a org.hibernate.sqm.domain.SqmExpressableTypeBasic)
. 10 - its SqmExpressableType would be an ORM BasicType<Integer> (as an SqmDomainTypeBasic)
Ultimately this decision is delegated to DomainMetamodel#resolveArithmeticType
(DomainMetamodel#resolveSumFunctionType is related).
|
Note
|
We need to consider ways to allow customization of the |