55package org .hibernate .boot .models .bind .internal .binders ;
66
77import java .util .ArrayList ;
8+ import java .util .LinkedHashMap ;
89import java .util .List ;
910import java .util .function .BiConsumer ;
1011import java .util .function .BiFunction ;
1112
13+ import org .hibernate .boot .models .AccessTypePlacementException ;
1214import org .hibernate .boot .models .bind .internal .sources .BasicValueSource ;
1315import org .hibernate .boot .models .bind .internal .sources .ColumnSource ;
1416import org .hibernate .boot .models .bind .internal .sources .ComponentSource ;
2325import org .hibernate .mapping .Property ;
2426import org .hibernate .mapping .Table ;
2527import org .hibernate .models .spi .ClassDetails ;
28+ import org .hibernate .models .spi .FieldDetails ;
2629import org .hibernate .models .spi .MemberDetails ;
30+ import org .hibernate .models .spi .MethodDetails ;
2731
32+ import jakarta .persistence .Access ;
33+ import jakarta .persistence .AccessType ;
2834import jakarta .persistence .AssociationOverride ;
2935import jakarta .persistence .Convert ;
36+ import jakarta .persistence .Transient ;
3037
3138/// Shared support for binding component-valued mappings.
3239///
@@ -74,6 +81,7 @@ List<Column> bindBasicProperties(
7481 component ,
7582 table ,
7683 "" ,
84+ determineComponentAccessType ( source .componentType (), ownerType .getAccessType () ),
7785 source ::columnSource ,
7886 source ::conversion ,
7987 (path , member ) -> source .associationOverride ( path ),
@@ -104,6 +112,7 @@ List<Column> bindBasicProperties(
104112 component ,
105113 table ,
106114 "" ,
115+ determineComponentAccessType ( componentType , ownerType .getAccessType () ),
107116 columnSourceResolver ,
108117 conversionResolver ,
109118 associationOverrideResolver ,
@@ -121,6 +130,7 @@ private List<Column> bindProperties(
121130 Component component ,
122131 Table table ,
123132 String pathPrefix ,
133+ AccessType accessType ,
124134 BiFunction <String , MemberDetails , ColumnSource > columnSourceResolver ,
125135 BiFunction <String , MemberDetails , Convert > conversionResolver ,
126136 BiFunction <String , MemberDetails , AssociationOverride > associationOverrideResolver ,
@@ -129,7 +139,7 @@ private List<Column> bindProperties(
129139 boolean nullableByDefault ,
130140 boolean updatable ) {
131141 final List <Column > columns = new ArrayList <>();
132- componentType . forEachPersistableMember ( ( member ) -> {
142+ for ( MemberDetails member : resolveComponentMembers ( componentType , accessType ) ) {
133143 validateMember ( member );
134144 final String attributeName = member .resolveAttributeName ();
135145 final String memberPath = pathPrefix + attributeName ;
@@ -154,7 +164,7 @@ private List<Column> bindProperties(
154164 property .setValue ( manyToOne );
155165 component .addProperty ( property );
156166 columns .addAll ( manyToOne .getColumns () );
157- return ;
167+ continue ;
158168 }
159169
160170 if ( isEmbeddedMember ( member ) ) {
@@ -173,6 +183,7 @@ private List<Column> bindProperties(
173183 nestedComponent ,
174184 table ,
175185 memberPath + "." ,
186+ determineComponentAccessType ( member .getType ().determineRawClass (), accessType ),
176187 columnSourceResolver ,
177188 conversionResolver ,
178189 associationOverrideResolver ,
@@ -181,7 +192,7 @@ private List<Column> bindProperties(
181192 nullableByDefault ,
182193 updatable
183194 ) );
184- return ;
195+ continue ;
185196 }
186197
187198 final BasicValue basicValue = createBasicValue (
@@ -202,10 +213,78 @@ private List<Column> bindProperties(
202213 );
203214 columnConsumer .accept ( member , column );
204215 columns .add ( column );
205- } );
216+ }
206217 return columns ;
207218 }
208219
220+ private AccessType determineComponentAccessType (ClassDetails componentType , AccessType containingAccessType ) {
221+ final Access access = componentType .getDirectAnnotationUsage ( Access .class );
222+ return access == null ? containingAccessType : access .value ();
223+ }
224+
225+ private List <MemberDetails > resolveComponentMembers (ClassDetails componentType , AccessType accessType ) {
226+ final LinkedHashMap <String , MemberDetails > results = new LinkedHashMap <>();
227+
228+ for ( FieldDetails field : componentType .getFields () ) {
229+ final Access access = field .getDirectAnnotationUsage ( Access .class );
230+ if ( access == null ) {
231+ continue ;
232+ }
233+ validateAttributeLevelAccess ( componentType , field , access .value () );
234+ if ( !isTransient ( field ) ) {
235+ results .put ( field .resolveAttributeName (), field );
236+ }
237+ }
238+
239+ for ( MethodDetails method : componentType .getMethods () ) {
240+ final Access access = method .getDirectAnnotationUsage ( Access .class );
241+ if ( access == null ) {
242+ continue ;
243+ }
244+ validateAttributeLevelAccess ( componentType , method , access .value () );
245+ if ( !isTransient ( method ) ) {
246+ results .put ( method .resolveAttributeName (), method );
247+ }
248+ }
249+
250+ if ( accessType == AccessType .FIELD ) {
251+ for ( FieldDetails field : componentType .getFields () ) {
252+ if ( field .isPersistable ()
253+ && !isTransient ( field )
254+ && !results .containsKey ( field .resolveAttributeName () ) ) {
255+ results .put ( field .resolveAttributeName (), field );
256+ }
257+ }
258+ }
259+ else {
260+ for ( MethodDetails method : componentType .getMethods () ) {
261+ if ( method .isPersistable ()
262+ && !isTransient ( method )
263+ && !results .containsKey ( method .resolveAttributeName () ) ) {
264+ results .put ( method .resolveAttributeName (), method );
265+ }
266+ }
267+ }
268+
269+ return new ArrayList <>( results .values () );
270+ }
271+
272+ private boolean isTransient (MemberDetails member ) {
273+ return member .hasDirectAnnotationUsage ( Transient .class );
274+ }
275+
276+ private void validateAttributeLevelAccess (
277+ ClassDetails componentType ,
278+ MemberDetails member ,
279+ AccessType attributeAccessType ) {
280+ if ( ( attributeAccessType == AccessType .FIELD && !member .isField () )
281+ || ( attributeAccessType == AccessType .PROPERTY && member .isField () )
282+ || ( attributeAccessType == AccessType .PROPERTY
283+ && member .asMethodDetails ().getMethodKind () != MethodDetails .MethodKind .GETTER ) ) {
284+ throw new AccessTypePlacementException ( componentType , member );
285+ }
286+ }
287+
209288 private void validateMember (MemberDetails member ) {
210289 if ( member .isPlural ()
211290 || member .hasDirectAnnotationUsage ( jakarta .persistence .OneToMany .class )
0 commit comments