Skip to content

Commit ea22fcd

Browse files
committed
HHH-16383 - NaturalIdClass
1 parent 6e98b48 commit ea22fcd

22 files changed

+582
-84
lines changed

hibernate-core/src/main/java/org/hibernate/NaturalIdLoadAccess.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.hibernate;
66

77
import jakarta.persistence.EntityGraph;
8-
98
import jakarta.persistence.PessimisticLockScope;
109
import jakarta.persistence.Timeout;
1110
import jakarta.persistence.metamodel.SingularAttribute;
@@ -35,7 +34,10 @@
3534
* @see Session#byNaturalId(Class)
3635
* @see org.hibernate.annotations.NaturalId
3736
* @see SimpleNaturalIdLoadAccess
37+
*
38+
* @deprecated (since 7.3) Use {@linkplain Session#findByNaturalId} instead.
3839
*/
40+
@Deprecated
3941
public interface NaturalIdLoadAccess<T> {
4042

4143
/**

hibernate-core/src/main/java/org/hibernate/NaturalIdMultiLoadAccess.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@
3636
*
3737
* @see Session#byMultipleNaturalId(Class)
3838
* @see org.hibernate.annotations.NaturalId
39+
*
40+
* @deprecated (since 7.3) Use {@linkplain Session#findMultipleByNaturalId} instead.
3941
*/
42+
@Deprecated
4043
public interface NaturalIdMultiLoadAccess<T> {
4144

4245
/**

hibernate-core/src/main/java/org/hibernate/Session.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,42 @@ public interface Session extends SharedSessionContract, EntityManager {
558558
*/
559559
Object find(String entityName, Object primaryKey, FindOption... options);
560560

561+
/// Find an entity by [natural-id][org.hibernate.annotations.NaturalId].
562+
///
563+
/// @param entityType The type of entity to load.
564+
/// @param naturalId The natural-id value.
565+
/// @param options The options to apply to the find operation.
566+
///
567+
/// @apiNote For non-aggregated composite natural-ids, consider leveraging a [org.hibernate.annotations.NaturalIdClass].
568+
<T> T findByNaturalId(Class<T> entityType, Object naturalId, FindOption... options);
569+
570+
/// Find an entity by [natural-id][org.hibernate.annotations.NaturalId].
571+
///
572+
/// @param entityName The name of the entity type to load.
573+
/// @param naturalId The natural-id value.
574+
/// @param options The options to apply to the find operation.
575+
///
576+
/// @apiNote For non-aggregated composite natural-ids, consider leveraging a [org.hibernate.annotations.NaturalIdClass].
577+
Object findByNaturalId(String entityName, Object naturalId, FindOption... options);
578+
579+
/// Find multiple entities by [natural-id][org.hibernate.annotations.NaturalId].
580+
///
581+
/// @param entityType The type of entity to load.
582+
/// @param naturalIds The natural-id values.
583+
/// @param options The options to apply to the find operation. May contain [FindMultipleOption] values.
584+
///
585+
/// @apiNote For non-aggregated composite natural-ids, consider leveraging a [org.hibernate.annotations.NaturalIdClass].
586+
<T> List<T> findMultipleByNaturalId(Class<T> entityType, List<Object> naturalIds, FindOption... options);
587+
588+
/// Find multiple entities by [natural-id][org.hibernate.annotations.NaturalId].
589+
///
590+
/// @param entityName The name of the entity type to load.
591+
/// @param naturalIds The natural-id values.
592+
/// @param options The options to apply to the find operation. May contain [FindMultipleOption] values.
593+
///
594+
/// @apiNote For non-aggregated composite natural-ids, consider leveraging a [org.hibernate.annotations.NaturalIdClass].
595+
List<Object> findMultipleByNaturalId(String entityName, List<Object> naturalIds, FindOption... options);
596+
561597
/**
562598
* Return the persistent instances of the given entity class with the given identifiers
563599
* as a list. The position of an instance in the returned list matches the position of its
@@ -1203,7 +1239,10 @@ public interface Session extends SharedSessionContract, EntityManager {
12031239
*
12041240
* @throws HibernateException If the given class does not resolve as a mapped entity,
12051241
* or if the entity does not declare a natural id
1242+
*
1243+
* @deprecated (since 7.3) : Use {@linkplain #findByNaturalId} instead.
12061244
*/
1245+
@Deprecated
12071246
<T> NaturalIdLoadAccess<T> byNaturalId(Class<T> entityClass);
12081247

12091248
/**
@@ -1218,7 +1257,10 @@ public interface Session extends SharedSessionContract, EntityManager {
12181257
*
12191258
* @throws HibernateException If the given name does not resolve to a mapped entity,
12201259
* or if the entity does not declare a natural id
1260+
*
1261+
* @deprecated (since 7.3) : Use {@linkplain #findByNaturalId} instead.
12211262
*/
1263+
@Deprecated
12221264
<T> NaturalIdLoadAccess<T> byNaturalId(String entityName);
12231265

12241266
/**
@@ -1233,7 +1275,10 @@ public interface Session extends SharedSessionContract, EntityManager {
12331275
*
12341276
* @throws HibernateException If the given class does not resolve as a mapped entity,
12351277
* or if the entity does not declare a natural id
1278+
*
1279+
* @deprecated (since 7.3) : Use {@linkplain #findByNaturalId} instead.
12361280
*/
1281+
@Deprecated
12371282
<T> SimpleNaturalIdLoadAccess<T> bySimpleNaturalId(Class<T> entityClass);
12381283

12391284
/**
@@ -1248,7 +1293,10 @@ public interface Session extends SharedSessionContract, EntityManager {
12481293
*
12491294
* @throws HibernateException If the given name does not resolve to a mapped entity,
12501295
* or if the entity does not declare a natural id
1296+
*
1297+
* @deprecated (since 7.3) : Use {@linkplain #findByNaturalId} instead.
12511298
*/
1299+
@Deprecated
12521300
<T> SimpleNaturalIdLoadAccess<T> bySimpleNaturalId(String entityName);
12531301

12541302
/**
@@ -1262,7 +1310,10 @@ public interface Session extends SharedSessionContract, EntityManager {
12621310
*
12631311
* @throws HibernateException If the given class does not resolve as a mapped entity,
12641312
* or if the entity does not declare a natural id
1313+
*
1314+
* @deprecated (since 7.3) : Use {@linkplain #findMultipleByNaturalId} instead.
12651315
*/
1316+
@Deprecated
12661317
<T> NaturalIdMultiLoadAccess<T> byMultipleNaturalId(Class<T> entityClass);
12671318

12681319
/**
@@ -1276,7 +1327,10 @@ public interface Session extends SharedSessionContract, EntityManager {
12761327
*
12771328
* @throws HibernateException If the given name does not resolve to a mapped entity,
12781329
* or if the entity does not declare a natural id
1330+
*
1331+
* @deprecated (since 7.3) : Use {@linkplain #findMultipleByNaturalId} instead.
12791332
*/
1333+
@Deprecated
12801334
<T> NaturalIdMultiLoadAccess<T> byMultipleNaturalId(String entityName);
12811335

12821336
/**

hibernate-core/src/main/java/org/hibernate/SimpleNaturalIdLoadAccess.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
* @see Session#bySimpleNaturalId(Class)
3030
* @see org.hibernate.annotations.NaturalId
3131
* @see NaturalIdLoadAccess
32+
*
33+
* @deprecated (since 7.3) Use {@linkplain Session#findByNaturalId} instead.
3234
*/
35+
@Deprecated
3336
public interface SimpleNaturalIdLoadAccess<T> {
3437

3538
/**

hibernate-core/src/main/java/org/hibernate/annotations/NaturalId.java

Lines changed: 82 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -4,87 +4,95 @@
44
*/
55
package org.hibernate.annotations;
66

7+
78
import java.lang.annotation.Retention;
89
import java.lang.annotation.Target;
910

1011
import static java.lang.annotation.ElementType.FIELD;
1112
import static java.lang.annotation.ElementType.METHOD;
1213
import static java.lang.annotation.RetentionPolicy.RUNTIME;
1314

14-
/**
15-
* Specifies that a field or property of an entity class is part of
16-
* the natural id of the entity. This annotation is very useful when
17-
* the primary key of an entity class is a surrogate key, that is,
18-
* a {@linkplain jakarta.persistence.GeneratedValue system-generated}
19-
* synthetic identifier, with no domain-model semantics. There should
20-
* always be some other field or combination of fields which uniquely
21-
* identifies an instance of the entity from the point of view of the
22-
* user of the system. This is the <em>natural id</em> of the entity.
23-
* <p>
24-
* A natural id may be a single field or property of the entity:
25-
* <pre>
26-
* &#64;Entity
27-
* &#64;Cache &#64;NaturalIdCache
28-
* class Person {
29-
*
30-
* //synthetic id
31-
* &#64;GeneratedValue @Id
32-
* Long id;
33-
*
34-
* &#64;NotNull
35-
* String name;
36-
*
37-
* //simple natural id
38-
* &#64;NotNull @NaturalId
39-
* String ssn;
40-
*
41-
* ...
42-
* }
43-
* </pre>
44-
* <p>
45-
* or it may be a composite value:
46-
* <pre>
47-
* &#64;Entity
48-
* &#64;Cache &#64;NaturalIdCache
49-
* class Vehicle {
50-
*
51-
* //synthetic id
52-
* &#64;GeneratedValue @Id
53-
* Long id;
54-
*
55-
* //composite natural id
56-
*
57-
* &#64;Enumerated
58-
* &#64;NotNull &#64;NaturalId
59-
* Region region;
60-
*
61-
* &#64;NotNull &#64;NaturalId
62-
* String registration;
63-
*
64-
* ...
65-
* }
66-
* </pre>
67-
* <p>
68-
* Unlike the {@linkplain jakarta.persistence.Id primary identifier}
69-
* of an entity, a natural id may be {@linkplain #mutable}.
70-
* <p>
71-
* On the other hand, a field or property which forms part of a natural
72-
* id may never be null, and so it's a good idea to use {@code @NaturalId}
73-
* in conjunction with the Bean Validation {@code @NotNull} annotation
74-
* or {@link jakarta.persistence.Basic#optional @Basic(optional=false)}.
75-
* <p>
76-
* The {@link org.hibernate.Session} interface offers several methods
77-
* that allow an entity instance to be retrieved by its
78-
* {@linkplain org.hibernate.Session#bySimpleNaturalId(Class) simple}
79-
* or {@linkplain org.hibernate.Session#byNaturalId(Class) composite}
80-
* natural id value. If the entity is also marked for {@linkplain
81-
* NaturalIdCache natural id caching}, then these methods may be able
82-
* to avoid a database round trip.
83-
*
84-
* @author Nicolás Lichtmaier
85-
*
86-
* @see NaturalIdCache
87-
*/
15+
/// Specifies that a field or property of an entity class is part of
16+
/// the natural id of the entity. This annotation is very useful when
17+
/// the primary key of an entity class is a surrogate key, that is,
18+
/// a {@linkplain jakarta.persistence.GeneratedValue system-generated}
19+
/// synthetic identifier, with no domain-model semantics. There should
20+
/// always be some other field or combination of fields which uniquely
21+
/// identifies an instance of the entity from the point of view of the
22+
/// user of the system. This is the _natural id_ of the entity.
23+
///
24+
/// A natural id may be a single (basic or embedded) attribute of the entity:
25+
/// ````java
26+
/// @Entity
27+
/// class Person {
28+
///
29+
/// //synthetic id
30+
/// @GeneratedValue @Id
31+
/// Long id;
32+
///
33+
/// @NotNull
34+
/// String name;
35+
///
36+
/// //simple natural id
37+
/// @NotNull @NaturalId
38+
/// String ssn;
39+
///
40+
/// ...
41+
/// }
42+
/// ```
43+
///
44+
/// or it may be a non-aggregated composite value:
45+
/// ```java
46+
/// @Entity
47+
/// class Vehicle {
48+
///
49+
/// //synthetic id
50+
/// @GeneratedValue @Id
51+
/// Long id;
52+
///
53+
/// //composite natural id
54+
///
55+
/// @Enumerated
56+
/// @NotNull
57+
/// @NaturalId
58+
/// Region region;
59+
///
60+
/// @NotNull
61+
/// @NaturalId
62+
/// String registration;
63+
///
64+
/// ...
65+
/// }
66+
/// ```
67+
///
68+
/// Unlike the {@linkplain jakarta.persistence.Id primary identifier}
69+
/// of an entity, a natural id may be {@linkplain #mutable}.
70+
///
71+
/// On the other hand, a field or property which forms part of a natural
72+
/// id may never be null, and so it's a good idea to use `@NaturalId`
73+
/// in conjunction with the Bean Validation `@NotNull` annotation
74+
/// or [@Basic(optional=false)][jakarta.persistence.Basic#optional()].
75+
///
76+
/// The [org.hibernate.Session] interface offers several methods
77+
/// that allow retrieval of one or more entity references by natural-id
78+
/// allow an entity instance to be retrieved by its natural-id:
79+
/// * [org.hibernate.Session#findByNaturalId] allows loading a single
80+
/// entity instance by natural-id.
81+
/// * [org.hibernate.Session#findMultipleByNaturalId] allows loading multiple
82+
/// entity instances by natural-id.
83+
///
84+
/// If the entity is also marked for [natural id caching][NaturalIdCache],
85+
/// then these methods may be able to avoid a database round trip.
86+
///
87+
/// @see org.hibernate.Session#findByNaturalId
88+
/// @see org.hibernate.Session#findMultipleByNaturalId
89+
/// @see NaturalIdClass
90+
/// @see NaturalIdCache
91+
///
92+
/// @apiNote For non-aggregated composite natural-id cases, it is recommended to
93+
/// leverage [@NaturalIdClass][NaturalIdClass] for loading.
94+
///
95+
/// @author Nicolás Lichtmaier
8896
@Target({METHOD, FIELD})
8997
@Retention(RUNTIME)
9098
public @interface NaturalId {
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.annotations;
6+
7+
import java.lang.annotation.ElementType;
8+
import java.lang.annotation.RetentionPolicy;
9+
import java.lang.annotation.Target;
10+
import java.lang.annotation.Retention;
11+
12+
/// Models a non-aggregated composite natural-id for the purpose of loading.
13+
/// The non-aggregated form uses multiple [@NaturalId][NaturalId] as opposed
14+
/// to the aggregated form which uses a single [@NaturalId][NaturalId] combined
15+
/// with [@Embedded][jakarta.persistence.Embedded].
16+
/// Functions in a similar fashion as [@IdClass][jakarta.persistence.IdClass] for
17+
/// non-aggregated composite identifiers.
18+
///
19+
/// ```java
20+
/// @Entity
21+
/// @NaturalIdClass(OrderNaturalId.class)
22+
/// class Order {
23+
/// @Id
24+
/// Integer id;
25+
/// @NaturalId @ManyToOne
26+
/// Customer customer;
27+
/// @NaturalId
28+
/// Integer orderNumber;
29+
/// ...
30+
/// }
31+
///
32+
/// class OrderNaturalId {
33+
/// Integer customer;
34+
/// Integer orderNumber;
35+
/// ...
36+
/// }
37+
/// ```
38+
///
39+
/// @see NaturalId
40+
/// @see jakarta.persistence.IdClass
41+
///
42+
/// @author Steve Ebersole
43+
@Target(ElementType.TYPE)
44+
@Retention(RetentionPolicy.RUNTIME)
45+
public @interface NaturalIdClass {
46+
/// The class to use for loading the associated entity by natural-id.
47+
Class<?> value();
48+
}

0 commit comments

Comments
 (0)