HHH-9998 - Continue documentation TLC - natural-id

This commit is contained in:
Steve Ebersole 2015-08-05 13:09:35 -05:00
parent 9b9b806aac
commit 603a410fdc
10 changed files with 252 additions and 20 deletions

View File

@ -9,12 +9,151 @@
version="5.0"
xml:lang="en"
xmlns="http://docbook.org/ns/docbook"
>
xmlns:xi="http://www.w3.org/2001/XInclude">
<title>Natural Ids</title>
<para>
* simple
* composite
* caching
* apis
Natural ids represent unique identifiers that naturally exist within your domain model. Even if
a natural id does not make a good primary key, it still is useful to tell Hibernate about it.
As we will see later, Hibernate provides a dedicated, efficient API for loading and entity by its natural-id
much like it offers for loading by identifier (PK).
</para>
<section xml:id="naturalid-mapping">
<title>Natural Id Mapping</title>
<para>
Natural ids are defined in terms of one or more persistent attributes.
</para>
<example>
<title>Natural id using single basic attribute</title>
<programlisting role="JAVA"><xi:include href="extras/SimpleBasicNaturalIdMapping.java" parse="text" /></programlisting>
</example>
<example>
<title>Natural id using single embedded attribute</title>
<programlisting role="JAVA"><xi:include href="extras/SimpleCompositeNaturalIdMapping.java" parse="text" /></programlisting>
</example>
<example>
<title>Natural id using multiple persistent attributes</title>
<programlisting role="JAVA"><xi:include href="extras/NonSimpleNaturalIdMapping.java" parse="text" /></programlisting>
</example>
</section>
<section xml:id="naturalid-api">
<title>Natural Id API</title>
<para>
As stated before, Hibernate provides an API for loading entities by natural id. This is represented by the
<interfacename>org.hibernate.NaturalIdLoadAccess</interfacename> contract obtained via
<methodname>Session#byNaturalId</methodname>. If the entity does not define a natural id, an exception
will be thrown there.
</para>
<example>
<title>Using NaturalIdLoadAccess</title>
<programlisting role="JAVA"><xi:include href="extras/NaturalIdLoadAccessUsage.java" parse="text" /></programlisting>
</example>
<para>
NaturalIdLoadAccess offers 2 distinct methods for obtaining the entity:
<itemizedlist>
<listitem>
<para>
<methodname>load</methodname> - obtains a reference to the entity, making sure
that the entity state is initialized.
</para>
</listitem>
<listitem>
<para>
<methodname>getReference</methodname> - obtains a reference to the entity. The state
may or may not be initialized. If the entity is associated with the Session already,
that reference (loaded or not) is returned; else if the entity supports proxy
generation, an uninitialized proxy is generated and returned; otherwise
the entity is loaded from the database and returned.
</para>
</listitem>
</itemizedlist>
</para>
<para>
NaturalIdLoadAccess also allows to request locking for the load. We might use that to load an
entity by natural id and at the same time apply a pessimistic lock. For additional details on locking,
see the <citetitle>Hibernate User Guide</citetitle>.
</para>
<para>
We will discuss the last method available on NaturalIdLoadAccess
(<methodname>setSynchronizationEnabled</methodname>) in <xref linkend="naturalid-mutability-caching"/>.
</para>
<para>
Because the Company and PostalCarrier entities define "simple" natural ids, we also allow simplified
access to load them based on the natural ids.
</para>
<example>
<title>Using SimpleNaturalIdLoadAccess</title>
<programlisting role="JAVA"><xi:include href="extras/SimpleNaturalIdLoadAccessUsage.java" parse="text" /></programlisting>
</example>
<para>
Here we see the use of the <interfacename>org.hibernate.SimpleNaturalIdLoadAccess</interfacename>
contract, obtained via <methodname>Session#bySimpleNaturalId</methodname>. SimpleNaturalIdLoadAccess is similar
to NaturalIdLoadAccess except that it does not define the <methodname>using</methodname> method. Instead,
because these "simple" natural ids are defined based on just one attribute we can directly pass the
corresponding value of that natural id attribute directly to the <methodname>load</methodname>
and <methodname>getReference</methodname> methods. If the entity does not define a natural id or if the
natural id it does define is not simple, an exception will be thrown there.
</para>
</section>
<section xml:id="naturalid-mutability-caching">
<title>Natural Id - Mutability and Caching</title>
<para>
A natural id may be mutable or immutable. By default <literal>@NaturalId</literal> marks
an immutable natural id. An immutable natural id is expected to never change values.
If the values of the natural id attribute(s) can change, <literal>@NaturalId(mutable=true)</literal>
should be used instead.
</para>
<example>
<title>Mutable natural id</title>
<programlisting role="JAVA"><xi:include href="extras/MutableNaturalIdMapping.java" parse="text" /></programlisting>
</example>
<para>
Within the Session, Hibernate maintains a mapping from natural id values to pk values. If natural ids
values have changed it is possible for this mapping to become out of date until a flush occurs. To work
around this condition, Hibernate will attempt to discover any such pending changes and adjust for them
when the <methodname>load</methodname> or <methodname>getReference</methodname> method is executed. To
be clear: this is only pertinent for mutable natural ids.
</para>
<para>
This "discovery and adjustment" have a performance impact. If an application is certain that none of its
mutable natural ids already associated with the Session have changed, it can disable that checking by
calling <methodname>setSynchronizationEnabled(false)</methodname> (the default is true). This will force
Hibernate to circumvent the checking of mutable natural ids.
</para>
<example>
<title>Mutable natural id synchronization use-case</title>
<programlisting role="JAVA"><xi:include href="extras/MutableNaturalIdSynchronization.java" parse="text" /></programlisting>
</example>
<para>
Not only can this NaturalId-to-PK resolution be cached in the Session, but we can also have it cached in
the second-level cache if second level caching is enabled.
</para>
<example>
<title>Natural id caching</title>
<programlisting role="JAVA"><xi:include href="extras/NaturalIdCaching.java" parse="text" /></programlisting>
</example>
</section>
</chapter>

View File

@ -0,0 +1,8 @@
@Entity
public class Person {
@Id
private Integer id;
@NaturalId(mutable=true)
private String ssn;
...
}

View File

@ -0,0 +1,17 @@
Session session = ...;
Person person = session.bySimpleNaturalId( Person.class )
.load( "123-45-6789" );
person.setSsn( "987-65-4321" );
...
// returns null!
person = session.bySimpleNaturalId( Person.class )
.setSynchronizationEnabled( false )
.load( "987-65-4321" );
// returns correctly!
person = session.bySimpleNaturalId( Person.class )
.setSynchronizationEnabled( true )
.load( "987-65-4321" );

View File

@ -0,0 +1,9 @@
@Entity
@NaturalIdCache
public class Company {
@Id
private Integer id;
@NaturalId
private String taxIdentifier;
...
}

View File

@ -0,0 +1,15 @@
Session session = ...;
Company company = session.byNaturalId( Company.class )
.using( "taxIdentifier", "abc-123-xyz" )
.load();
PostalCarrier carrier = session.byNaturalId( PostalCarrier.class )
.using( "postalCode", new PostalCode( ... ) )
.load();
Department department = ...;
Course course = session.byNaturalId( Course.class )
.using( "department", department )
.using( "code", "101" )
.load();

View File

@ -0,0 +1,11 @@
@Entity
public class Course {
@Id
private Integer id;
@NaturalId
@ManyToOne
private Department department;
@NaturalId
private String code;
...
}

View File

@ -0,0 +1,8 @@
@Entity
public class Company {
@Id
private Integer id;
@NaturalId
private String taxIdentifier;
...
}

View File

@ -0,0 +1,15 @@
@Entity
public class PostalCarrier {
@Id
private Integer id;
@NaturalId
@Embedded
private PostalCode postalCode;
...
}
@Embeddable
public class PostalCode {
...
}

View File

@ -0,0 +1,7 @@
Session session = ...;
Company company = session.bySimpleNaturalId( Company.class )
.load( "abc-123-xyz" );
PostalCarrier carrier = session.bySimpleNaturalId( PostalCarrier.class )
.load( new PostalCode( ... ) );

View File

@ -4,20 +4,23 @@ Status of the documentation overhaul (5.0 version)
Overall the plan is to define 3 DocBook-based guides. The intention is for this document to serve
as an outline of the work and a status of what still needs done.
NOTE : entries marked with <strike>strike-through</strike> indicate that the content is believed to be done; review
would still be appreciated.
User Guide
==========
Covers reference topics targeting users.
* Prefix (done)
* Architecture (done)
* DomainModel (done)
* Bootstrap (done)
* PersistenceContext (done)
* Database_Access (done)
* Transactions (done)
* JNDI (done)
* <strike>Prefix</strike>
* <strike>Architecture</strike>
* <strike>DomainModel</strike>
* <strike>Bootstrap</strike>
* <strike>PersistenceContext</strike>
* <strike>Database_Access</strike>
* <strike>Transactions</strike>
* <strike>JNDI</strike>
* Locking (needs some work)
* Fetching (needs some work)
* Batching (needs lot of work - not started - open questions)
@ -28,7 +31,7 @@ Covers reference topics targeting users.
* Native_Queries (needs lots of work)
* Multi_Tenancy (needs some work)
* OSGi (right place for this?)
* Envers
* Envers (right place for this?)
* Portability (needs some work)
@ -39,15 +42,15 @@ Covers mapping domain model to database. Note that a lot of the "not started" c
matter of pulling that content in and better organizing it.
* Prefix (done)
* Data_Categorizations (done)
* Basic_Types (done)
* Composition (done)
* Collection (needs some work)
* <strike>Prefix</strike>
* <strike>Data_Categorizations</strike>
* <strike>Basic_Types</strike>
* <strike>Composition</strike>
* <strike>Collection (needs some work)
* Entity (needs some work)
* Secondary_Tables (not started)
* Identifiers (mostly done - needs "derived id" stuff documented)
* Natural_Id (not started)
* <strike>Natural_Id</strike>
* Associations (not started)
* Attribute_Access (not started)
* Mapping_Overrides - AttributeOverrides/AssociationOverrides (not started)