HHH-9998 - Continue documentation TLC - mapping identifiers

This commit is contained in:
Steve Ebersole 2015-08-03 18:29:16 -05:00
parent 4f725332af
commit 4fadceca43
13 changed files with 495 additions and 21 deletions

View File

@ -47,6 +47,10 @@
<xi:include href="chapters/services/Services.xml" />
<!--
org.hibernate.boot.model.IdGeneratorStrategyInterpreter
custom Session/SessionFactory implementators
<xi:include href="chapters/types/Custom_Types.xml" />
-->

View File

@ -193,4 +193,8 @@
JPA portability
* HQL/JPQL differences
* naming strategies
* basic types
* simple id types
* generated id types
* "embedded composite identifiers"
</chapter>

View File

@ -7,12 +7,15 @@
~ See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
-->
<chapter xml:id="basic" xmlns="http://docbook.org/ns/docbook" xmlns:xi="http://www.w3.org/2001/XInclude">
<title>Basic Types</title>
<abstract>
This chapter will discuss actual basic type mappings as well as how to override those
mappings and provide extra mappings.
</abstract>
<info>
<title>Basic Types</title>
<abstract>
<para>
This chapter will discuss actual basic type mappings as well as how to override those
mappings and provide extra mappings.
</para>
</abstract>
</info>
<para>
Basic value types usually map a single database value, or column, to a single, non-aggregated Java
@ -1002,7 +1005,7 @@
<title>UUID as identifier</title>
<para>
Hibernate supports using UUID values as identifiers. They can even be generated! For
details see the discussion of generators in <xref linkend="identifiers"/>
details see the discussion of generators in <xref linkend="identifiers-generators"/>
</para>
</section>
</section>

View File

@ -9,12 +9,27 @@
version="5.0"
xml:lang="en"
xmlns="http://docbook.org/ns/docbook"
>
<title>Identifiers</title>
xmlns:xi="http://www.w3.org/2001/XInclude">
<info>
<title>Identifiers</title>
<abstract>
<para>
This chapter discusses the characteristics of entity identifier attributes and modelling
them.
</para>
</abstract>
</info>
<para>
Identifiers model the primary key of an entity. They are used to uniquely identify each
specific entity. Every entity must define an identifier.
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.
</para>
<note>
@ -31,23 +46,401 @@
<!-- todo : be sure to discuss generators in simple, then reference from composite -->
<section>
<section xml:id="identifiers-simple">
<title>Simple identifiers</title>
<para></para>
<para>
Simple identifiers map to a single basic attribute, and are denoted using the
<interfacename>javax.persistence.Id</interfacename> annotation.
</para>
<para>
According to JPA only the following types should be used as identifier attribute types:
<itemizedlist>
<listitem><para>any Java primitive type</para></listitem>
<listitem><para>any primitive wrapper type</para></listitem>
<listitem><para>java.lang.String</para></listitem>
<listitem><para>java.util.Date (TemporalType#DATE)</para></listitem>
<listitem><para>java.sql.Date</para></listitem>
<listitem><para>java.math.BigDecimal</para></listitem>
<listitem><para>java.math.BigInteger</para></listitem>
</itemizedlist>
Any types used for identifier attributes beyond this list will not be portable.
</para>
<para>
Values for simple identifiers can be assigned, as we have seen in the examples above. The expectation
for assigned identifier values is that the application assigns (sets them on the entity attribute) prior
to calling save/persist.
</para>
<example>
<title>Simple assigned identifier</title>
<programlisting role="JAVA"><xi:include href="extras/SimpleAssigned.java" parse="text" /></programlisting>
</example>
<para>
Values for simple identifiers can be generated. To denote that an identifier attribute is
generated, it is annotated with <interfacename>javax.persistence.GeneratedValue</interfacename>
</para>
<example>
<title>Simple generated identifier</title>
<programlisting role="JAVA"><xi:include href="extras/SimpleGenerated.java" parse="text" /></programlisting>
</example>
<para>
Additionally to the type restriction list above, JPA
says that if using generated identifier values (see below) only integer types (short, int, long) will be
portably supported.
</para>
<para>
The expectation for generated identifier values is that Hibernate will generate the value
when the save/persist occurs.
</para>
<para>
Identifier value generations strategies are discussed in detail in <xref linkend="identifiers-generators"/>.
</para>
</section>
<section>
<section xml:id="identifiers-composite">
<title>Composite identifiers</title>
<para></para>
<section>
<title>Composite identifiers - aggregated</title>
<para></para>
<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.
<itemizedlist>
<listitem>
<para>
The composite identifier must be represented by a "primary key class". The primary key class
may be defined using the <interfacename>javax.persistence.EmbeddedId</interfacename> annotation
(see <xref linkend="identifiers-composite-aggregated"/>) or defined using the
<interfacename>javax.persistence.IdClass</interfacename> annotation (see
<xref linkend="identifiers-composite-nonaggregated"/>).
</para>
</listitem>
<listitem>
<para>
The primary key class must be public and must have a public no-arg constructor.
</para>
</listitem>
<listitem>
<para>
The primary key class must be serializable.
</para>
</listitem>
<listitem>
<para>
The primary key class must define equals and hashCode methods, consistent with equality for
the underlying database types to which the key is mapped.
</para>
</listitem>
</itemizedlist>
</para>
<section xml:id="identifiers-composite-aggregated">
<title>Composite identifiers - aggregated (EmbeddedId)</title>
<para>
<!-- todo : write -->
blah blah blah
</para>
</section>
<section>
<title>Composite identifiers - non-aggregated</title>
<para></para>
<section xml:id="identifiers-composite-nonaggregated">
<title>Composite identifiers - non-aggregated (IdClass)</title>
<para>
<!-- todo : write -->
blah blah blah
</para>
</section>
</section>
<section xml:id="identifiers-generators">
<title>Generated identifier values</title>
<para>
Hibernate supports identifier value generation across a number of different types. Remember
that JPA portably defines identifier value generation just for integer types.
</para>
<para>
Identifier value generation is indicates using the <interfacename>javax.persistence.GeneratedValue</interfacename>
annotation. The most important piece of information here is the specified
<interfacename>javax.persistence.GenerationType</interfacename> which indicates how values will be generated.
</para>
<note>
<para>
The discussions below assume that the application is using Hibernate's "new generator mappings" as
indicated by the <literal>hibernate.id.new_generator_mappings</literal> setting or
<methodname>MetadataBuilder.enableNewIdentifierGeneratorSupport</methodname> method during bootstrap.
For legacy reasons the default value for this setting is currently false, however we anticipate it
becoming true at some point. The rest of the discussion here assumes this setting is enabled (true).
</para>
</note>
<itemizedlist>
<title>GenerationTypes</title>
<listitem>
<para>
<literal>AUTO</literal> (the default) - Indicates that the persistence provider (Hibernate) should
chose an appropriate generation strategy. See <xref linkend="identifiers-generators-auto"/>.
</para>
</listitem>
<listitem>
<para>
<literal>IDENTITY</literal> - Indicates that database IDENTITY columns will be used for
primary key value generation. See <xref linkend="identifiers-generators-identity"/>.
</para>
</listitem>
<listitem>
<para>
<literal>SEQUENCE</literal> - Indicates that database sequence should be used for obtaining
primary key values. See <xref linkend="identifiers-generators-sequence"/>.
</para>
</listitem>
<listitem>
<para>
<literal>TABLE</literal> - Indicates that a database table should be used for obtaining
primary key values. See <xref linkend="identifiers-generators-table"/>.
</para>
</listitem>
</itemizedlist>
<section xml:id="identifiers-generators-auto">
<title>Interpreting AUTO</title>
<para>
How a persistence provider interprets the AUTO generation type is left up to the provider. Hibernate
interprets it in the following order:
<itemizedlist>
<listitem>
<para>
If the given name matches the name for a <interfacename>javax.persistence.SequenceGenerator</interfacename>
annotation -> <xref linkend="identifiers-generators-sequence"/>.
</para>
</listitem>
<listitem>
<para>
If the given name matches the name for a <interfacename>javax.persistence.TableGenerator</interfacename>
annotation -> <xref linkend="identifiers-generators-table"/>.
</para>
</listitem>
<listitem>
<para>
If the given name matches the name for a <interfacename>org.hibernate.annotations.GenericGenerator</interfacename>
annotation -> <xref linkend="identifiers-generators-generic"/>.
</para>
</listitem>
</itemizedlist>
The fallback is to consult with the pluggable <interfacename>org.hibernate.boot.model.IdGeneratorStrategyInterpreter</interfacename>
contract, which is covered in detail in the <citetitle>Hibernate Integrations Guide</citetitle>. The default
behavior is to look at the java type of the identifier attribute:
<itemizedlist>
<listitem>
<para>
If it is UUID -> <xref linkend="identifiers-generators-uuid"/>
</para>
</listitem>
<listitem>
<para>
Otherwise -> <xref linkend="identifiers-generators-sequence"/>
</para>
</listitem>
</itemizedlist>
</para>
</section>
<section xml:id="identifiers-generators-sequence">
<title>Using sequences</title>
<para>
For implementing database sequence-based identifier value generation Hibernate makes use of its
<classname>org.hibernate.id.enhanced.SequenceStyleGenerator</classname> id generator. It is important
to note that SequenceStyleGenerator is capable of working against databases that do not support sequences
by switching to a table as the underlying backing. This gives Hibernate a huge degree of portability
across databases while still maintaining consistent id generation behavior (versus say choosing
between sequence and IDENTITY). This backing storage is completely transparent to the user.
</para>
<para>
The preferred (and portable) way to configure this generator is using the JPA-defined
<interfacename>javax.persistence.SequenceGenerator</interfacename> annotation.
</para>
<para>
The simplest form is to simply request sequence generation; Hibernate will use a single, implicitly-named
sequence (<literal>hibernate_sequence</literal>) for all such unnamed definitions.
</para>
<example>
<title>Unnamed sequence</title>
<programlisting role="JAVA"><xi:include href="extras/UnnamedSequence.java" parse="text" /></programlisting>
</example>
<para>
Or a specifically named sequence can be requested
</para>
<example>
<title>Named sequence</title>
<programlisting role="JAVA"><xi:include href="extras/NamedSequence.java" parse="text" /></programlisting>
</example>
<para>
Use <interfacename>javax.persistence.SequenceGenerator</interfacename> to specify additional configuration.
</para>
<example>
<title>Configured sequence</title>
<programlisting role="JAVA"><xi:include href="extras/ConfiguredSequence.java" parse="text" /></programlisting>
</example>
<!-- todo : SequenceStyleGenerator specific config (optimizer, forceTable, etc) -->
</section>
<section xml:id="identifiers-generators-identity">
<title>Using IDENTITY columns</title>
<para>
For implementing identifier value generation based on IDENTITY columns, Hibernate makes use of its
<classname>org.hibernate.id.IdentityGenerator</classname> id generator which expects the identifier
to generated by INSERT into the table. IdentityGenerator understands 3 different ways that the
INSERT-generated value might be retrieved:
<itemizedlist>
<listitem>
<para>
If Hibernate believes the JDBC environment supports <methodname>java.sql.Statement#getGeneratedKeys</methodname>,
then that approach will be used for extracting the IDENTITY generated keys.
</para>
</listitem>
<listitem>
<para>
Otherwise, if <methodname>Dialect#supportsInsertSelectIdentity</methodname> reports
true, Hibernate will use the Dialect specific INSERT+SELECT statement syntax.
</para>
</listitem>
<listitem>
<para>
Otherwise, Hibernate will expect that the database supports some form of asking
for the most recently inserted IDENTITY value via a separate SQL command as
indicated by <methodname>Dialect#getIdentitySelectString</methodname>
</para>
</listitem>
</itemizedlist>
</para>
<para>
It is important to realize that this imposes a runtime behavior where the entity row *must* be
physically inserted prior to the identifier value being known. This can mess up extended persistence
contexts (conversations). Because of the runtime imposition/inconsistency Hibernate suggest other
forms of identifier value generation be used.
</para>
</section>
<section xml:id="identifiers-generators-table">
<title>Using identifier table</title>
<para>
Hibernate achieves table-based identifier generation based on its
<interfacename>org.hibernate.id.enhanced.TableGenerator</interfacename> id generator which defines
a table capable of holding multiple named value segments for any number of entities.
</para>
<example>
<title>Table generator table structure</title>
<programlisting role="SQL"><xi:include href="extras/TableGenerator.sql" parse="text" /></programlisting>
</example>
<para>
The basic idea is that a given table-generator table (<literal>hibernate_sequences</literal> for example)
can hold multiple segments of identifier generation values.
</para>
<example>
<title>Unnamed table generator</title>
<programlisting role="JAVA"><xi:include href="extras/UnnamedTable.java" parse="text" /></programlisting>
</example>
<para>
If no table name is given Hibernate assumes an implicit name of <literal>hibernate_sequences</literal>.
Additionally, because no <methodname>javax.persistence.TableGenerator#pkColumnValue</methodname> is
specified, Hibernate will use the default segment (<literal>sequence_name='default'</literal>) from the
hibernate_sequences table.
</para>
<!-- todo : discuss setting org.hibernate.id.enhanced.TableGenerator specific settings -->
</section>
<section xml:id="identifiers-generators-uuid">
<title>Using UUID generation</title>
<para>
As mentioned above, Hibernate supports UUID identifier value generation. This is supported through its
<classname>org.hibernate.id.UUIDGenerator</classname> id generator.
</para>
<para>
UUIDGenerator supports pluggable strategies for exactly how the UUID is generated. These strategies
are defined by the <interfacename>org.hibernate.id.UUIDGenerationStrategy</interfacename> contract.
The default strategy is a version 4 (random) strategy according to IETF RFC 4122. Hibernate does ship
with an alternative strategy which is a RFC 4122 version 1 (time-based) strategy (using ip address
rather than mac address).
</para>
<example>
<title>Implicitly using the random UUID strategy</title>
<programlisting role="JAVA"><xi:include href="extras/UUIDRandom.java" parse="text" /></programlisting>
</example>
<para>
To specify an alternative generation strategy, we'd have to define some configuration via
@GenericGenerator. Here we choose the RFC 4122 version 1 compliant strategy named
<classname>org.hibernate.id.uuid.CustomVersionOneStrategy</classname>
</para>
<example>
<title>Implicitly using the random UUID strategy</title>
<programlisting role="JAVA"><xi:include href="extras/UUIDCustomVersionOneStrategy.java" parse="text" /></programlisting>
</example>
</section>
<section xml:id="identifiers-generators-generic">
<title>Using @GenericGenerator</title>
<para>
@GenericGenerator allows integration of any Hibernate <interfacename>org.hibernate.id.IdentifierGenerator</interfacename>
implementation, including any of the specific ones discussed here and any custom ones.
</para>
</section>
<para>
For discussion of generated values for non-identifier attributes, see <xref linkend="generated"/>
</para>
</section>
<section xml:id="identifiers-derived">
<title>Derived Identifiers</title>
<para>
<!-- todo : write -->
Ugh...
</para>
</section>
</chapter>

View File

@ -0,0 +1,8 @@
@Entity
public class MyEntity {
@Id
@GeneratedValue(generation=SEQUENCE, name="my_sequence")
@SequenceGenerator( name = "my_sequence", schema = "globals", allocationSize = 30 )
public Integer id;
...
}

View File

@ -0,0 +1,7 @@
@Entity
public class MyEntity {
@Id
@GeneratedValue(generation=SEQUENCE, name="my_sequence")
public Integer id;
...
}

View File

@ -0,0 +1,6 @@
@Entity
public class MyEntity {
@Id
public Integer id;
...
}

View File

@ -0,0 +1,7 @@
@Entity
public class MyEntity {
@Id
@GeneratedValue
public Integer id;
...
}

View File

@ -0,0 +1,4 @@
create table hibernate_sequences(
sequence_name VARCHAR NOT NULL,
next_val INTEGER NOT NULL
)

View File

@ -0,0 +1,17 @@
@Entity
public class MyEntity {
@Id
@GeneratedValue( generator="uuid" )
@GenericGenerator(
name="uuid",
strategy="org.hibernate.id.UUIDGenerator",
parameters = {
@Parameter(
name="uuid_gen_strategy_class",
value="org.hibernate.id.uuid.CustomVersionOneStrategy"
)
}
)
public UUID id;
...
}

View File

@ -0,0 +1,7 @@
@Entity
public class MyEntity {
@Id
@GeneratedValue
public UUID id;
...
}

View File

@ -0,0 +1,7 @@
@Entity
public class MyEntity {
@Id
@GeneratedValue(generation=SEQUENCE)
public Integer id;
...
}

View File

@ -0,0 +1,7 @@
@Entity
public class MyEntity {
@Id
@GeneratedValue(generation=TABLE)
public Integer id;
...
}