HHH-10022 - Continue documentation TLC (part 2)

This commit is contained in:
Steve Ebersole 2015-08-06 14:07:50 -05:00
parent bd6bb7b841
commit 8dc14436af
8 changed files with 265 additions and 31 deletions

View File

@ -23,13 +23,31 @@
<para>
Identifiers model the primary key of an entity. They are used to uniquely identify each specific entity.
JPA defines the behavior of changing the value of the identifier attribute to be undefined; Hibernate simply
does not support that.
</para>
<para>
Every entity must define an identifier. For entity inheritance hierarchies, the identifier must be
defined just on the entity that is the root of the hierarchy.
Hibernate and JPA both make the following assumptions about the corresponding database column(s):
<itemizedlist>
<listitem>
<para>
<literal>UNIQUE</literal> - The values must uniquely identify each row.
</para>
</listitem>
<listitem>
<para>
<literal>NOT NULL</literal> - The values cannot be null. For composite ids, no part can be null.
</para>
</listitem>
<listitem>
<para>
<literal>IMMUTABLE</literal> - The values, once inserted, can never be changed. This is more
a general guide, than a hard-fast rule as opinions vary. JPA defines the behavior of changing the
value of the identifier attribute to be undefined; Hibernate simply does not support that. In cases
where the values for the PK you have chosen will be updated, Hibernate recommends mapping the
mutable value as a natural id, and use a surrogate id for the PK. See <xref linkend="naturalid"/>.
</para>
</listitem>
</itemizedlist>
</para>
<note>
@ -41,10 +59,13 @@
</note>
<para>
An identifier might be simple (single value) or composite (multiple values).
Every entity must define an identifier. For entity inheritance hierarchies, the identifier must be
defined just on the entity that is the root of the hierarchy.
</para>
<!-- todo : be sure to discuss generators in simple, then reference from composite -->
<para>
An identifier might be simple (single value) or composite (multiple values).
</para>
<section xml:id="identifiers-simple">
<title>Simple identifiers</title>
@ -108,23 +129,8 @@
<title>Composite identifiers</title>
<para>
Composite identifiers correspond to one or more persistent attributes. A primary key class must be
defined to represent a composite primary key. Composite primary keys typically arise when mapping from
legacy databases when the database key is comprised of several columns. The EmbeddedId
or IdClass annotation is used to denote a composite primary key. See Sections 11.1.17 and
11.1.22.
</para>
<note>
<para>
The restriction that a composite identifier has to be represented by a "primary key class" is
a JPA restriction. Hibernate does allow composite identifiers to be defined without a
"primary key class", but use of that modeling technique is deprecated and not discussed here.
</para>
</note>
<para>
Here are the rules governing composite identifiers, as defined by the JPA specification.
Composite identifiers correspond to one or more persistent attributes. Here are the rules governing
composite identifiers, as defined by the JPA specification.
<itemizedlist>
<listitem>
<para>
@ -154,20 +160,93 @@
</itemizedlist>
</para>
<note>
<para>
The restriction that a composite identifier has to be represented by a "primary key class" is
a JPA restriction. Hibernate does allow composite identifiers to be defined without a
"primary key class", but use of that modeling technique is deprecated and not discussed here.
</para>
</note>
<para>
The attributes making up the composition can be either basic, composite, ManyToOne. Note especially
that collections and one-to-ones are never appropriate.
<!-- todo : discuss why a real one-to-one is never appropriate? -->
</para>
<section xml:id="identifiers-composite-aggregated">
<title>Composite identifiers - aggregated (EmbeddedId)</title>
<para>
<!-- todo : write -->
blah blah blah
Modelling a composite identifier using an EmbeddedId simply means defining an
Embeddable to be a composition for the the one or more attributes making up the
identifier and then exposing an attribute of that Embeddable type on the entity.
</para>
<example>
<title>Basic EmbeddedId</title>
<programlisting role="JAVA"><xi:include href="extras/EmbeddedId1.java" parse="text" /></programlisting>
</example>
<para>
As mentioned before, EmbeddedIds can even contain ManyToOne attributes.
</para>
<example>
<title>EmbeddedId with ManyToOne</title>
<programlisting role="JAVA"><xi:include href="extras/EmbeddedId2.java" parse="text" /></programlisting>
</example>
<note>
<para>
Hibernate supports directly modeling the ManyToOne in the PK class, whether EmbeddedId or IdClass.
However that is not portably supported by the JPA specification. In JPA terms one would
use "derived identifiers"; for details, see <xref linkend="identifiers-derived"/>.
</para>
</note>
</section>
<section xml:id="identifiers-composite-nonaggregated">
<title>Composite identifiers - non-aggregated (IdClass)</title>
<para>
<!-- todo : write -->
blah blah blah
Modelling a composite identifier using an IdClass differs from using an EmbeddedId in that the entity
defines each individual attribute making up the composition. The IdClass simply acts as a "shadow".
</para>
<example>
<title>Basic IdClass</title>
<programlisting role="JAVA"><xi:include href="extras/IdClass1.java" parse="text" /></programlisting>
</example>
<para>
Non-aggregated composite identifiers can also contain ManyToOne attributes as we saw with aggregated
ones (still non-portably)
</para>
<example>
<title>IdClass with ManyToOne</title>
<programlisting role="JAVA"><xi:include href="extras/IdClass2.java" parse="text" /></programlisting>
</example>
<para>
With non-aggregated composite identifiers, Hibernate also supports "partial" generation of the
composite values.
</para>
<example>
<title>IdClass with partial generation</title>
<programlisting role="JAVA"><xi:include href="extras/IdClass3.java" parse="text" /></programlisting>
</example>
<note>
<para>
This feature exists because of a highly questionable interpretation of the JPA specification
made by the SpecJ committee. Hibernate does not feel that JPA defines support for this, but
added the feature simply to be usable in SpecJ benchmarks. Use of this feature may or may not
be portable from a JPA perspective.
</para>
</note>
</section>
</section>
@ -175,6 +254,14 @@
<section xml:id="identifiers-generators">
<title>Generated identifier values</title>
<!-- todo : discuss select generator -->
<note>
<para>
For discussion of generated values for non-identifier attributes, see <xref linkend="generated"/>
</para>
</note>
<para>
Hibernate supports identifier value generation across a number of different types. Remember
that JPA portably defines identifier value generation just for integer types.
@ -431,9 +518,75 @@
</para>
</section>
<para>
For discussion of generated values for non-identifier attributes, see <xref linkend="generated"/>
</para>
<section xml:id="identifiers-generators-optimizer">
<title>Optimizers</title>
<para>
Most of the Hibernate generators that separately obtain identifier values from database structures
support the use of pluggable optimizers. Optimizers help manage the number of times Hibernate
has to talk to the database in order to generate identifier values. For example, with no optimizer
applied to a sequence-generator, everytime the application asked Hibernate to generate an identifier
it would need to grab the next sequence value from the database. But if we can minimize the
number of times we need to communicate with the database here, the application will be able to perform
better. Which is in fact the role of these optimizers.
</para>
<variablelist>
<varlistentry>
<term>none</term>
<listitem>
<para>
No optimization is performed. We communicate with the database each and every time
an identifier value is needed from the generator.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>pooled-lo</term>
<listitem>
<para>
The pooled-lo optimizer works on the principle that the increment-value is encoded into
the database table/sequence structure. In sequence-terms this means that the sequence
is defined with a greater-that-1 increment size. For example, consider a brand new sequence
defined as <literal>create sequence my_sequence start with 1 increment by 20</literal>.
This sequence essentially defines a "pool" of 20 usable id values each and every time
we ask it for its next-value. The pooled-lo optimizer interprets the next-value as the
low end of that pool. So when we first ask it for next-value, we'd get 1. We then assume
that the valid pool would be the values from 1-20 inclusive. The next call to
the sequence would result in 21, which would define 21-40 as the valid range. And so on.
The "lo" part of the name indicates that the value from the database table/sequence is
interpreted as the pool lo(w) end.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>pooled</term>
<listitem>
<para>
Just like pooled-lo, except that here the value from the table/sequence is interpreted
as the high end of the value pool.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>hilo</term>
<term>legacy-hilo</term>
<listitem>
<para>
Define a custom algorithm for generating pools of values based on a single value from
a table or sequence. These optimizers are not recommended for use. They are maintained
(and mentioned) here simply for use by legacy applications that used these strategies
previously.
</para>
</listitem>
</varlistentry>
</variablelist>
<para>
Applications can also implement and use their own optimizer strategies, as defined by the
<interfacename>org.hibernate.id.enhanced.Optimizer</interfacename> contract.
</para>
</section>
</section>
<section xml:id="identifiers-derived">

View File

@ -0,0 +1,13 @@
@Entity
public class Login {
@Embeddable
public static class PK implements Serializable {
private String system;
private String username;
...
}
@EmbeddedId
private PK pk;
...
}

View File

@ -0,0 +1,14 @@
@Entity
public class Login {
@Embeddable
public static class PK implements Serializable {
@ManyToOne
private System system;
private String username;
...
}
@EmbeddedId
private PK pk;
...
}

View File

@ -0,0 +1,15 @@
@Entity
@IdClass(PK.class)
public class Login {
public static class PK implements Serializable {
private String system;
private String username;
...
}
@Id
private String system;
@Id
private String username;
...
}

View File

@ -0,0 +1,17 @@
@Entity
@IdClass(PK.class)
public class Login {
public static class PK implements Serializable {
private System system;
private String username;
...
}
@Id
@ManyToOne
private System system;
@Id
private String username;
...
}

View File

@ -0,0 +1,20 @@
@Entity
@IdClass(PK.class)
public class LogFile {
public static class PK implements Serializable {
private String name;
private LocalDate date;
private Integer uniqueStamp;
...
}
@Id
private String name;
@Id
private LocalDate date;
@Id
@GeneratedValue
private Integer uniqueStamp;
...
}

View File

@ -196,5 +196,6 @@
* basic types
* simple id types
* generated id types
* composite ids and many-to-one
* "embedded composite identifiers"
</chapter>

View File

@ -21,8 +21,9 @@ Covers reference topics targeting users.
* <strike>Database_Access</strike>
* <strike>Transactions</strike>
* <strike>JNDI</strike>
* Locking (needs some work)
* Fetching (needs some work)
* Cascading (needs lots of work)
* Locking (needs some work)
* Batching (needs lot of work - not started - open questions)
* Caching (needs some work)
* Events (need some work)