Skip to content

Commit 603a410

Browse files
committed
HHH-9998 - Continue documentation TLC - natural-id
1 parent 9b9b806 commit 603a410

10 files changed

+252
-20
lines changed

documentation/src/main/docbook/mappingGuide/en-US/chapters/natural_id/Natural_Id.xml

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,151 @@
99
version="5.0"
1010
xml:lang="en"
1111
xmlns="http://docbook.org/ns/docbook"
12-
>
12+
xmlns:xi="http://www.w3.org/2001/XInclude">
1313
<title>Natural Ids</title>
14+
1415
<para>
15-
* simple
16-
* composite
17-
* caching
18-
* apis
16+
Natural ids represent unique identifiers that naturally exist within your domain model. Even if
17+
a natural id does not make a good primary key, it still is useful to tell Hibernate about it.
18+
As we will see later, Hibernate provides a dedicated, efficient API for loading and entity by its natural-id
19+
much like it offers for loading by identifier (PK).
1920
</para>
21+
22+
<section xml:id="naturalid-mapping">
23+
<title>Natural Id Mapping</title>
24+
25+
<para>
26+
Natural ids are defined in terms of one or more persistent attributes.
27+
</para>
28+
29+
<example>
30+
<title>Natural id using single basic attribute</title>
31+
<programlisting role="JAVA"><xi:include href="extras/SimpleBasicNaturalIdMapping.java" parse="text" /></programlisting>
32+
</example>
33+
34+
<example>
35+
<title>Natural id using single embedded attribute</title>
36+
<programlisting role="JAVA"><xi:include href="extras/SimpleCompositeNaturalIdMapping.java" parse="text" /></programlisting>
37+
</example>
38+
39+
<example>
40+
<title>Natural id using multiple persistent attributes</title>
41+
<programlisting role="JAVA"><xi:include href="extras/NonSimpleNaturalIdMapping.java" parse="text" /></programlisting>
42+
</example>
43+
44+
</section>
45+
46+
<section xml:id="naturalid-api">
47+
<title>Natural Id API</title>
48+
49+
<para>
50+
As stated before, Hibernate provides an API for loading entities by natural id. This is represented by the
51+
<interfacename>org.hibernate.NaturalIdLoadAccess</interfacename> contract obtained via
52+
<methodname>Session#byNaturalId</methodname>. If the entity does not define a natural id, an exception
53+
will be thrown there.
54+
</para>
55+
56+
<example>
57+
<title>Using NaturalIdLoadAccess</title>
58+
<programlisting role="JAVA"><xi:include href="extras/NaturalIdLoadAccessUsage.java" parse="text" /></programlisting>
59+
</example>
60+
61+
<para>
62+
NaturalIdLoadAccess offers 2 distinct methods for obtaining the entity:
63+
<itemizedlist>
64+
<listitem>
65+
<para>
66+
<methodname>load</methodname> - obtains a reference to the entity, making sure
67+
that the entity state is initialized.
68+
</para>
69+
</listitem>
70+
<listitem>
71+
<para>
72+
<methodname>getReference</methodname> - obtains a reference to the entity. The state
73+
may or may not be initialized. If the entity is associated with the Session already,
74+
that reference (loaded or not) is returned; else if the entity supports proxy
75+
generation, an uninitialized proxy is generated and returned; otherwise
76+
the entity is loaded from the database and returned.
77+
</para>
78+
</listitem>
79+
</itemizedlist>
80+
</para>
81+
82+
<para>
83+
NaturalIdLoadAccess also allows to request locking for the load. We might use that to load an
84+
entity by natural id and at the same time apply a pessimistic lock. For additional details on locking,
85+
see the <citetitle>Hibernate User Guide</citetitle>.
86+
</para>
87+
88+
<para>
89+
We will discuss the last method available on NaturalIdLoadAccess
90+
(<methodname>setSynchronizationEnabled</methodname>) in <xref linkend="naturalid-mutability-caching"/>.
91+
</para>
92+
93+
<para>
94+
Because the Company and PostalCarrier entities define "simple" natural ids, we also allow simplified
95+
access to load them based on the natural ids.
96+
</para>
97+
98+
<example>
99+
<title>Using SimpleNaturalIdLoadAccess</title>
100+
<programlisting role="JAVA"><xi:include href="extras/SimpleNaturalIdLoadAccessUsage.java" parse="text" /></programlisting>
101+
</example>
102+
103+
<para>
104+
Here we see the use of the <interfacename>org.hibernate.SimpleNaturalIdLoadAccess</interfacename>
105+
contract, obtained via <methodname>Session#bySimpleNaturalId</methodname>. SimpleNaturalIdLoadAccess is similar
106+
to NaturalIdLoadAccess except that it does not define the <methodname>using</methodname> method. Instead,
107+
because these "simple" natural ids are defined based on just one attribute we can directly pass the
108+
corresponding value of that natural id attribute directly to the <methodname>load</methodname>
109+
and <methodname>getReference</methodname> methods. If the entity does not define a natural id or if the
110+
natural id it does define is not simple, an exception will be thrown there.
111+
</para>
112+
</section>
113+
114+
<section xml:id="naturalid-mutability-caching">
115+
<title>Natural Id - Mutability and Caching</title>
116+
<para>
117+
A natural id may be mutable or immutable. By default <literal>@NaturalId</literal> marks
118+
an immutable natural id. An immutable natural id is expected to never change values.
119+
If the values of the natural id attribute(s) can change, <literal>@NaturalId(mutable=true)</literal>
120+
should be used instead.
121+
</para>
122+
123+
<example>
124+
<title>Mutable natural id</title>
125+
<programlisting role="JAVA"><xi:include href="extras/MutableNaturalIdMapping.java" parse="text" /></programlisting>
126+
</example>
127+
128+
<para>
129+
Within the Session, Hibernate maintains a mapping from natural id values to pk values. If natural ids
130+
values have changed it is possible for this mapping to become out of date until a flush occurs. To work
131+
around this condition, Hibernate will attempt to discover any such pending changes and adjust for them
132+
when the <methodname>load</methodname> or <methodname>getReference</methodname> method is executed. To
133+
be clear: this is only pertinent for mutable natural ids.
134+
</para>
135+
136+
<para>
137+
This "discovery and adjustment" have a performance impact. If an application is certain that none of its
138+
mutable natural ids already associated with the Session have changed, it can disable that checking by
139+
calling <methodname>setSynchronizationEnabled(false)</methodname> (the default is true). This will force
140+
Hibernate to circumvent the checking of mutable natural ids.
141+
</para>
142+
143+
<example>
144+
<title>Mutable natural id synchronization use-case</title>
145+
<programlisting role="JAVA"><xi:include href="extras/MutableNaturalIdSynchronization.java" parse="text" /></programlisting>
146+
</example>
147+
148+
<para>
149+
Not only can this NaturalId-to-PK resolution be cached in the Session, but we can also have it cached in
150+
the second-level cache if second level caching is enabled.
151+
</para>
152+
153+
<example>
154+
<title>Natural id caching</title>
155+
<programlisting role="JAVA"><xi:include href="extras/NaturalIdCaching.java" parse="text" /></programlisting>
156+
</example>
157+
158+
</section>
20159
</chapter>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@Entity
2+
public class Person {
3+
@Id
4+
private Integer id;
5+
@NaturalId(mutable=true)
6+
private String ssn;
7+
...
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
Session session = ...;
2+
3+
Person person = session.bySimpleNaturalId( Person.class )
4+
.load( "123-45-6789" );
5+
person.setSsn( "987-65-4321" );
6+
7+
...
8+
9+
// returns null!
10+
person = session.bySimpleNaturalId( Person.class )
11+
.setSynchronizationEnabled( false )
12+
.load( "987-65-4321" );
13+
14+
// returns correctly!
15+
person = session.bySimpleNaturalId( Person.class )
16+
.setSynchronizationEnabled( true )
17+
.load( "987-65-4321" );
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@Entity
2+
@NaturalIdCache
3+
public class Company {
4+
@Id
5+
private Integer id;
6+
@NaturalId
7+
private String taxIdentifier;
8+
...
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Session session = ...;
2+
3+
Company company = session.byNaturalId( Company.class )
4+
.using( "taxIdentifier", "abc-123-xyz" )
5+
.load();
6+
7+
PostalCarrier carrier = session.byNaturalId( PostalCarrier.class )
8+
.using( "postalCode", new PostalCode( ... ) )
9+
.load();
10+
11+
Department department = ...;
12+
Course course = session.byNaturalId( Course.class )
13+
.using( "department", department )
14+
.using( "code", "101" )
15+
.load();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@Entity
2+
public class Course {
3+
@Id
4+
private Integer id;
5+
@NaturalId
6+
@ManyToOne
7+
private Department department;
8+
@NaturalId
9+
private String code;
10+
...
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
@Entity
2+
public class Company {
3+
@Id
4+
private Integer id;
5+
@NaturalId
6+
private String taxIdentifier;
7+
...
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@Entity
2+
public class PostalCarrier {
3+
@Id
4+
private Integer id;
5+
@NaturalId
6+
@Embedded
7+
private PostalCode postalCode;
8+
...
9+
10+
}
11+
12+
@Embeddable
13+
public class PostalCode {
14+
...
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Session session = ...;
2+
3+
Company company = session.bySimpleNaturalId( Company.class )
4+
.load( "abc-123-xyz" );
5+
6+
PostalCarrier carrier = session.bySimpleNaturalId( PostalCarrier.class )
7+
.load( new PostalCode( ... ) );

documentation/status.md

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,23 @@ Status of the documentation overhaul (5.0 version)
44
Overall the plan is to define 3 DocBook-based guides. The intention is for this document to serve
55
as an outline of the work and a status of what still needs done.
66

7+
NOTE : entries marked with <strike>strike-through</strike> indicate that the content is believed to be done; review
8+
would still be appreciated.
9+
710

811
User Guide
912
==========
1013

1114
Covers reference topics targeting users.
1215

13-
* Prefix (done)
14-
* Architecture (done)
15-
* DomainModel (done)
16-
* Bootstrap (done)
17-
* PersistenceContext (done)
18-
* Database_Access (done)
19-
* Transactions (done)
20-
* JNDI (done)
16+
* <strike>Prefix</strike>
17+
* <strike>Architecture</strike>
18+
* <strike>DomainModel</strike>
19+
* <strike>Bootstrap</strike>
20+
* <strike>PersistenceContext</strike>
21+
* <strike>Database_Access</strike>
22+
* <strike>Transactions</strike>
23+
* <strike>JNDI</strike>
2124
* Locking (needs some work)
2225
* Fetching (needs some work)
2326
* Batching (needs lot of work - not started - open questions)
@@ -28,7 +31,7 @@ Covers reference topics targeting users.
2831
* Native_Queries (needs lots of work)
2932
* Multi_Tenancy (needs some work)
3033
* OSGi (right place for this?)
31-
* Envers
34+
* Envers (right place for this?)
3235
* Portability (needs some work)
3336

3437

@@ -39,15 +42,15 @@ Covers mapping domain model to database. Note that a lot of the "not started" c
3942
matter of pulling that content in and better organizing it.
4043

4144

42-
* Prefix (done)
43-
* Data_Categorizations (done)
44-
* Basic_Types (done)
45-
* Composition (done)
46-
* Collection (needs some work)
45+
* <strike>Prefix</strike>
46+
* <strike>Data_Categorizations</strike>
47+
* <strike>Basic_Types</strike>
48+
* <strike>Composition</strike>
49+
* <strike>Collection (needs some work)
4750
* Entity (needs some work)
4851
* Secondary_Tables (not started)
4952
* Identifiers (mostly done - needs "derived id" stuff documented)
50-
* Natural_Id (not started)
53+
* <strike>Natural_Id</strike>
5154
* Associations (not started)
5255
* Attribute_Access (not started)
5356
* Mapping_Overrides - AttributeOverrides/AssociationOverrides (not started)

0 commit comments

Comments
 (0)