Skip to content

Commit c636c83

Browse files
committed
HHH-16651 cleaner separation of "fetch method" vs "fetch timing"
1 parent 7ed0241 commit c636c83

File tree

16 files changed

+228
-67
lines changed

16 files changed

+228
-67
lines changed

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

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
package org.hibernate.annotations;
88

99
/**
10-
* Enumerates strategies for fetching an association from the database.
10+
* Enumerates methods for fetching an association from the database.
1111
* <p>
1212
* The JPA-defined {@link jakarta.persistence.FetchType} enumerates the
1313
* possibilities for <em>when</em> an association might be fetched. This
@@ -46,7 +46,7 @@ public enum FetchMode {
4646
* when it is almost certain that the associated data will be
4747
* available in the second-level cache.
4848
*/
49-
SELECT( org.hibernate.FetchMode.SELECT ),
49+
SELECT,
5050

5151
/**
5252
* Use an outer join to load all instances of the related entity
@@ -62,7 +62,7 @@ public enum FetchMode {
6262
* since the associated data is retrieved as part of the initial
6363
* query.
6464
*/
65-
JOIN( org.hibernate.FetchMode.JOIN ),
65+
JOIN,
6666

6767
/**
6868
* Use a secondary select with a subselect that re-executes an
@@ -84,15 +84,11 @@ public enum FetchMode {
8484
* re-execution of the initial query within a SQL subselect.
8585
* </ul>
8686
*/
87-
SUBSELECT( org.hibernate.FetchMode.SELECT );
88-
89-
private final org.hibernate.FetchMode hibernateFetchMode;
90-
91-
FetchMode(org.hibernate.FetchMode hibernateFetchMode) {
92-
this.hibernateFetchMode = hibernateFetchMode;
93-
}
87+
SUBSELECT;
9488

9589
public org.hibernate.FetchMode getHibernateFetchMode() {
96-
return hibernateFetchMode;
90+
return this == JOIN
91+
? org.hibernate.FetchMode.JOIN
92+
: org.hibernate.FetchMode.SELECT;
9793
}
9894
}

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66
*/
77
package org.hibernate.annotations;
88

9+
import jakarta.persistence.FetchType;
10+
911
import java.lang.annotation.Repeatable;
1012
import java.lang.annotation.Retention;
1113
import java.lang.annotation.Target;
1214

15+
import static jakarta.persistence.FetchType.EAGER;
1316
import static java.lang.annotation.ElementType.PACKAGE;
1417
import static java.lang.annotation.ElementType.TYPE;
1518
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -91,8 +94,11 @@
9194
FetchOverride[] fetchOverrides() default {};
9295

9396
/**
94-
* Overrides the fetching strategy pf a particular association
95-
* in the named fetch profile being defined.
97+
* Overrides the fetching strategy of a particular association in
98+
* the named fetch profile being defined. If {@link #mode} and
99+
* {@link #fetch} are both unspecified, the strategy defaults to
100+
* {@linkplain FetchType#EAGER eager} {@linkplain FetchMode#JOIN join}
101+
* fetching.
96102
*/
97103
@Target({ TYPE, PACKAGE })
98104
@Retention(RUNTIME)
@@ -110,9 +116,15 @@
110116
String association();
111117

112118
/**
113-
* The {@linkplain FetchMode fetching strategy} to apply to
114-
* the association in the fetch profile being defined.
119+
* The {@linkplain FetchMode method} used for fetching the
120+
* association in the fetch profile being defined.
115121
*/
116122
FetchMode mode() default JOIN;
123+
124+
/**
125+
* The {@link FetchType timing} of association fetching in
126+
* the fetch profile being defined.
127+
*/
128+
FetchType fetch() default EAGER;
117129
}
118130
}

hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotationBinder.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import java.util.Locale;
1313
import java.util.Map;
1414

15+
import jakarta.persistence.FetchType;
1516
import org.hibernate.AnnotationException;
1617
import org.hibernate.MappingException;
1718
import org.hibernate.annotations.CollectionTypeRegistration;
@@ -22,6 +23,7 @@
2223
import org.hibernate.annotations.ConverterRegistrations;
2324
import org.hibernate.annotations.EmbeddableInstantiatorRegistration;
2425
import org.hibernate.annotations.EmbeddableInstantiatorRegistrations;
26+
import org.hibernate.annotations.FetchMode;
2527
import org.hibernate.annotations.FetchProfile;
2628
import org.hibernate.annotations.FetchProfile.FetchOverride;
2729
import org.hibernate.annotations.FetchProfiles;
@@ -852,7 +854,11 @@ private static void bindFetchProfile(FetchProfile fetchProfile, MetadataBuilding
852854
final String name = fetchProfile.name();
853855
if ( reuseOrCreateFetchProfile( context, name ) ) {
854856
for ( FetchOverride fetch : fetchProfile.fetchOverrides() ) {
855-
// TODO: validate which modes are valid where
857+
if ( fetch.fetch() == FetchType.LAZY && fetch.mode() == FetchMode.JOIN ) {
858+
throw new AnnotationException( "Fetch profile '" + name
859+
+ "' has a '@FetchOverride' with 'fetch=LAZY' and 'mode=JOIN'"
860+
+ " (join fetching is eager by nature)");
861+
}
856862
context.getMetadataCollector()
857863
.addSecondPass( new FetchOverrideSecondPass( name, fetch, context ) );
858864
}

hibernate-core/src/main/java/org/hibernate/boot/model/internal/FetchOverrideSecondPass.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import org.hibernate.mapping.FetchProfile;
1414
import org.hibernate.mapping.PersistentClass;
1515

16-
import java.util.Locale;
1716
import java.util.Map;
1817

1918
/**
@@ -43,9 +42,12 @@ public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws
4342
final FetchProfile profile = buildingContext.getMetadataCollector().getFetchProfile( fetchProfileName );
4443
// we already know that the FetchProfile exists and is good to use
4544
profile.addFetch(
46-
fetch.entity().getName(),
47-
fetch.association(),
48-
fetch.mode().toString().toLowerCase(Locale.ROOT)
45+
new FetchProfile.Fetch(
46+
fetch.entity().getName(),
47+
fetch.association(),
48+
fetch.mode(),
49+
fetch.fetch()
50+
)
4951
);
5052
}
5153
}

hibernate-core/src/main/java/org/hibernate/engine/FetchTiming.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
*/
77
package org.hibernate.engine;
88

9+
import jakarta.persistence.FetchType;
10+
911
/**
1012
* Enumeration of values describing <em>when</em> fetching should occur.
1113
*
@@ -20,5 +22,16 @@ public enum FetchTiming {
2022
/**
2123
* Performing fetching later, when needed. Also called lazy fetching.
2224
*/
23-
DELAYED
25+
DELAYED;
26+
27+
public static FetchTiming forType(FetchType type) {
28+
switch ( type ) {
29+
case EAGER:
30+
return IMMEDIATE;
31+
case LAZY:
32+
return DELAYED;
33+
default:
34+
throw new IllegalArgumentException( "Unknown FetchType" );
35+
}
36+
}
2437
}

hibernate-core/src/main/java/org/hibernate/engine/profile/Fetch.java

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,50 @@
66
*/
77
package org.hibernate.engine.profile;
88

9+
import org.hibernate.AssertionFailure;
10+
import org.hibernate.annotations.FetchMode;
11+
import org.hibernate.engine.FetchStyle;
12+
import org.hibernate.engine.FetchTiming;
13+
914
import java.util.Locale;
1015

16+
import static org.hibernate.engine.FetchTiming.IMMEDIATE;
17+
1118
/**
1219
* Models an individual fetch override within a {@link FetchProfile}.
1320
*
1421
* @author Steve Ebersole
1522
*/
1623
public class Fetch {
1724
private final Association association;
18-
private final Style style;
25+
private final FetchStyle method;
26+
private final FetchTiming timing;
1927

2028
/**
2129
* Constructs a {@link Fetch}.
2230
*
2331
* @param association The association to be fetched
2432
* @param style How to fetch it
33+
*
34+
* @deprecated use {@link #Fetch(Association,FetchStyle,FetchTiming)}
2535
*/
36+
@Deprecated(forRemoval = true)
2637
public Fetch(Association association, Style style) {
2738
this.association = association;
28-
this.style = style;
39+
this.method = style.toFetchStyle();
40+
this.timing = IMMEDIATE;
41+
}
42+
43+
/**
44+
* Constructs a {@link Fetch}.
45+
*
46+
* @param association The association to be fetched
47+
* @param method How to fetch it
48+
*/
49+
public Fetch(Association association, FetchStyle method, FetchTiming timing) {
50+
this.association = association;
51+
this.method = method;
52+
this.timing = timing;
2953
}
3054

3155
/**
@@ -37,14 +61,34 @@ public Association getAssociation() {
3761

3862
/**
3963
* The fetch style applied to the association.
64+
*
65+
* @deprecated use {@link #getMethod()}
4066
*/
67+
@Deprecated(forRemoval = true)
4168
public Style getStyle() {
42-
return style;
69+
return Style.fromFetchStyle( method );
70+
}
71+
72+
/**
73+
* The fetch method to be applied to the association.
74+
*/
75+
public FetchStyle getMethod() {
76+
return method;
77+
}
78+
79+
/**
80+
* The fetch timing to be applied to the association.
81+
*/
82+
public FetchTiming getTiming() {
83+
return timing;
4384
}
4485

4586
/**
4687
* The type or style of fetch.
88+
*
89+
* @deprecated Use {@link FetchStyle}
4790
*/
91+
@Deprecated(forRemoval = true)
4892
public enum Style {
4993
/**
5094
* Fetch via a join
@@ -59,6 +103,32 @@ public enum Style {
59103
*/
60104
SUBSELECT;
61105

106+
public FetchStyle toFetchStyle() {
107+
switch (this) {
108+
case SELECT:
109+
return FetchStyle.SELECT;
110+
case SUBSELECT:
111+
return FetchStyle.SUBSELECT;
112+
case JOIN:
113+
return FetchStyle.JOIN;
114+
default:
115+
throw new AssertionFailure("Unknown Fetch.Style");
116+
}
117+
}
118+
119+
static Style fromFetchStyle(FetchStyle fetchStyle) {
120+
switch (fetchStyle) {
121+
case SELECT:
122+
return SELECT;
123+
case SUBSELECT:
124+
return SUBSELECT;
125+
case JOIN:
126+
return JOIN;
127+
default:
128+
throw new IllegalArgumentException("Unhandled FetchStyle");
129+
}
130+
}
131+
62132
@Override
63133
public String toString() {
64134
return name().toLowerCase(Locale.ROOT);
@@ -79,10 +149,23 @@ public static Style parse(String name) {
79149
}
80150
return JOIN;
81151
}
152+
153+
public static Style forMethod(FetchMode fetchMode) {
154+
switch ( fetchMode ) {
155+
case JOIN:
156+
return JOIN;
157+
case SELECT:
158+
return SELECT;
159+
case SUBSELECT:
160+
return SUBSELECT;
161+
default:
162+
throw new IllegalArgumentException( "Unknown FetchMode" );
163+
}
164+
}
82165
}
83166

84167
@Override
85168
public String toString() {
86-
return "Fetch[" + style + "{" + association.getRole() + "}]";
169+
return "Fetch[" + method + "{" + association.getRole() + "}]";
87170
}
88171
}

hibernate-core/src/main/java/org/hibernate/engine/profile/FetchProfile.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import org.hibernate.type.BagType;
1616
import org.hibernate.type.Type;
1717

18+
import static org.hibernate.engine.FetchStyle.JOIN;
19+
1820
/**
1921
* The runtime representation of a Hibernate
2022
* {@linkplain org.hibernate.annotations.FetchProfile fetch profile}
@@ -97,7 +99,7 @@ public void addFetch(final Fetch fetch) {
9799

98100
// couple of things for which to account in the case of collection
99101
// join fetches
100-
if ( Fetch.Style.JOIN == fetch.getStyle() ) {
102+
if ( fetch.getMethod() == JOIN ) {
101103
// first, if this is a bag we need to ignore it if we previously
102104
// processed collection join fetches
103105
if ( associationType instanceof BagType ) {

hibernate-core/src/main/java/org/hibernate/engine/profile/internal/FetchProfileAffectee.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
*/
77
package org.hibernate.engine.profile.internal;
88

9-
import org.hibernate.engine.profile.Fetch;
10-
119
/**
1210
* Commonality between entities and collections as something that can be affected by fetch profiles.
1311
*
@@ -17,5 +15,5 @@ public interface FetchProfileAffectee {
1715
/**
1816
* Register the profile name with the entity/collection
1917
*/
20-
void registerAffectingFetchProfile(String fetchProfileName, Fetch.Style fetchStyle);
18+
void registerAffectingFetchProfile(String fetchProfileName);
2119
}

hibernate-core/src/main/java/org/hibernate/engine/spi/LoadQueryInfluencers.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import org.hibernate.persister.entity.EntityPersister;
2727

2828
import static java.util.Collections.emptySet;
29-
import static org.hibernate.engine.profile.Fetch.Style.SUBSELECT;
29+
import static org.hibernate.engine.FetchStyle.SUBSELECT;
3030

3131
/**
3232
* Centralize all options which can influence the SQL query needed to load an
@@ -318,7 +318,7 @@ private boolean isSubselectFetchEnabledInProfile(CollectionPersister persister)
318318
final FetchProfile fetchProfile = sessionFactory.getFetchProfile( profile );
319319
if ( fetchProfile != null ) {
320320
final Fetch fetch = fetchProfile.getFetchByRole( persister.getRole() );
321-
if ( fetch != null && fetch.getStyle() == SUBSELECT ) {
321+
if ( fetch != null && fetch.getMethod() == SUBSELECT) {
322322
return true;
323323
}
324324
}
@@ -347,7 +347,7 @@ private boolean hasSubselectLoadableCollectionsEnabledInProfile(EntityPersister
347347
final FetchProfile fetchProfile = sessionFactory.getFetchProfile( profile );
348348
for ( Fetch fetch : fetchProfile.getFetches().values() ) {
349349
// TODO: check that it's relevant to this persister??
350-
if ( fetch.getStyle() == SUBSELECT ) {
350+
if ( fetch.getMethod() == SUBSELECT ) {
351351
return true;
352352
}
353353
}

0 commit comments

Comments
 (0)