1010
1111import org .hibernate .AnnotationException ;
1212import org .hibernate .MappingException ;
13- import org .hibernate .annotations .LazyGroup ;
1413import org .hibernate .annotations .NotFoundAction ;
15- import org .hibernate .annotations .OnDeleteAction ;
16- import org .hibernate .annotations .common .reflection .XClass ;
17- import org .hibernate .annotations .common .reflection .XProperty ;
1814import org .hibernate .boot .spi .MetadataBuildingContext ;
1915import org .hibernate .boot .spi .PropertyData ;
2016import org .hibernate .boot .spi .SecondPass ;
2824import org .hibernate .mapping .PersistentClass ;
2925import org .hibernate .mapping .Property ;
3026import org .hibernate .mapping .SortableValue ;
31- import org .hibernate .type .ForeignKeyDirection ;
32-
33- import jakarta .persistence .ForeignKey ;
27+ import org .hibernate .mapping .Value ;
3428
3529import static org .hibernate .boot .model .internal .BinderHelper .checkMappedByType ;
3630import static org .hibernate .boot .model .internal .BinderHelper .findPropertyByName ;
3731import static org .hibernate .boot .model .internal .BinderHelper .getPath ;
38- import static org .hibernate .boot .model .internal .ToOneBinder .bindForeignKeyNameAndDefinition ;
39- import static org .hibernate .boot .model .internal .ToOneBinder .defineFetchingStrategy ;
40- import static org .hibernate .boot .model .internal .ToOneBinder .getReferenceEntityName ;
4132import static org .hibernate .internal .util .StringHelper .qualify ;
42- import static org .hibernate .type .ForeignKeyDirection .FROM_PARENT ;
43- import static org .hibernate .type .ForeignKeyDirection .TO_PARENT ;
4433
4534/**
4635 * We have to handle {@link jakarta.persistence.OneToOne} associations
4736 * in a second pass.
4837 */
4938public class OneToOneSecondPass implements SecondPass {
5039 private final MetadataBuildingContext buildingContext ;
40+ private final OneToOne oneToOne ;
41+ private final PropertyBinder binder ;
42+ private final Property property ;
5143 private final String mappedBy ;
5244 private final String ownerEntity ;
5345 private final PropertyHolder propertyHolder ;
54- private final NotFoundAction notFoundAction ;
5546 private final PropertyData inferredData ;
56- private final XClass targetEntity ;
57- private final OnDeleteAction onDeleteAction ;
58- private final boolean optional ;
59- private final String cascadeStrategy ;
47+ private final NotFoundAction notFoundAction ;
6048 private final AnnotatedJoinColumns joinColumns ;
49+ private final boolean annotatedEntity ;
6150
6251 public OneToOneSecondPass (
52+ PropertyBinder binder ,
53+ Property property ,
54+ OneToOne oneToOne ,
6355 String mappedBy ,
6456 String ownerEntity ,
65- @ SuppressWarnings ("unused" ) String ownerProperty ,
6657 PropertyHolder propertyHolder ,
6758 PropertyData inferredData ,
68- XClass targetEntity ,
59+ boolean annotatedEntity ,
6960 NotFoundAction notFoundAction ,
70- OnDeleteAction onDeleteAction ,
71- boolean optional ,
72- String cascadeStrategy ,
7361 AnnotatedJoinColumns columns ,
7462 MetadataBuildingContext buildingContext ) {
63+ this .binder = binder ;
64+ this .property = property ;
65+ this .oneToOne = oneToOne ;
7566 this .ownerEntity = ownerEntity ;
7667 this .mappedBy = mappedBy ;
7768 this .propertyHolder = propertyHolder ;
7869 this .buildingContext = buildingContext ;
7970 this .notFoundAction = notFoundAction ;
8071 this .inferredData = inferredData ;
81- this .targetEntity = targetEntity ;
82- this .onDeleteAction = onDeleteAction ;
83- this .optional = optional ;
84- this .cascadeStrategy = cascadeStrategy ;
72+ this .annotatedEntity = annotatedEntity ;
8573 this .joinColumns = columns ;
8674 }
8775
8876 @ Override
8977 public void doSecondPass (Map <String , PersistentClass > persistentClasses ) throws MappingException {
90- final OneToOne value = new OneToOne (
91- buildingContext ,
92- propertyHolder .getTable (),
93- propertyHolder .getPersistentClass ()
94- );
95- final String propertyName = inferredData .getPropertyName ();
96- value .setPropertyName ( propertyName );
97- final String referencedEntityName = getReferenceEntityName ( inferredData , targetEntity , buildingContext );
98- value .setReferencedEntityName ( referencedEntityName );
99- XProperty property = inferredData .getProperty ();
100- defineFetchingStrategy ( value , property , inferredData , propertyHolder );
101- //value.setFetchMode( fetchMode );
102- value .setOnDeleteAction ( onDeleteAction );
103- //value.setLazy( fetchMode != FetchMode.JOIN );
104-
105- value .setConstrained ( !optional );
106- value .setForeignKeyType ( getForeignKeyDirection () );
107- bindForeignKeyNameAndDefinition ( value , property , property .getAnnotation ( ForeignKey .class ), buildingContext );
108-
109- final PropertyBinder binder = new PropertyBinder ();
110- binder .setName ( propertyName );
111- binder .setProperty ( property );
112- binder .setValue ( value );
113- binder .setCascade ( cascadeStrategy );
114- binder .setAccessType ( inferredData .getDefaultAccess () );
115- binder .setBuildingContext ( buildingContext );
116-
117- final LazyGroup lazyGroupAnnotation = property .getAnnotation ( LazyGroup .class );
118- if ( lazyGroupAnnotation != null ) {
119- binder .setLazyGroup ( lazyGroupAnnotation .value () );
120- }
121-
122- final Property result = binder .makeProperty ();
123- result .setOptional ( optional );
12478 if ( mappedBy == null ) {
125- bindOwned ( persistentClasses , value , propertyName , result );
79+ bindOwned ( persistentClasses , oneToOne , inferredData . getPropertyName () );
12680 }
12781 else {
128- bindUnowned ( persistentClasses , value , result );
82+ bindUnowned ( persistentClasses , oneToOne );
12983 }
130- value .sortProperties ();
84+ oneToOne .sortProperties ();
13185 }
13286
133- private ForeignKeyDirection getForeignKeyDirection () {
134- return mappedBy == null ? FROM_PARENT : TO_PARENT ;
135- }
136-
137- private void bindUnowned (Map <String , PersistentClass > persistentClasses , OneToOne oneToOne , Property property ) {
87+ private void bindUnowned (Map <String , PersistentClass > persistentClasses , OneToOne oneToOne ) {
13888 oneToOne .setMappedByProperty ( mappedBy );
139- final PersistentClass targetEntity = persistentClasses .get ( oneToOne .getReferencedEntityName () );
89+ final String targetEntityName = oneToOne .getReferencedEntityName ();
90+ final PersistentClass targetEntity = persistentClasses .get ( targetEntityName );
14091 if ( targetEntity == null ) {
92+ final String problem = annotatedEntity
93+ ? " which does not belong to the same persistence unit"
94+ : " which is not an '@Entity' type" ;
14195 throw new MappingException ( "Association '" + getPath ( propertyHolder , inferredData )
142- + "' targets unknown entity type '" + oneToOne . getReferencedEntityName () + "'" );
96+ + "' targets the type '" + targetEntityName + "'" + problem );
14397 }
14498 final Property targetProperty = targetProperty ( oneToOne , targetEntity );
145- if ( targetProperty .getValue () instanceof OneToOne ) {
146- propertyHolder .addProperty ( property , inferredData .getDeclaringClass () );
147- }
148- else if ( targetProperty .getValue () instanceof ManyToOne ) {
149- bindTargetManyToOne ( persistentClasses , oneToOne , property , targetEntity , targetProperty );
99+ final Value targetPropertyValue = targetProperty .getValue ();
100+ if ( targetPropertyValue instanceof ManyToOne ) {
101+ bindTargetManyToOne ( persistentClasses , oneToOne , targetEntity , targetProperty );
150102 }
151- else {
103+ else if ( !( targetPropertyValue instanceof OneToOne ) ) {
152104 throw new AnnotationException ( "Association '" + getPath ( propertyHolder , inferredData )
153- + "' is 'mappedBy' a property named '" + mappedBy
154- + "' of the target entity type '" + oneToOne . getReferencedEntityName ()
155- + "' which is not a '@OneToOne' or '@ManyToOne' association" );
105+ + "' is 'mappedBy' a property named '" + mappedBy
106+ + "' of the target entity type '" + targetEntityName
107+ + "' which is not a '@OneToOne' or '@ManyToOne' association" );
156108 }
157109 checkMappedByType (
158110 mappedBy ,
@@ -166,7 +118,6 @@ else if ( targetProperty.getValue() instanceof ManyToOne ) {
166118 private void bindTargetManyToOne (
167119 Map <String , PersistentClass > persistentClasses ,
168120 OneToOne oneToOne ,
169- Property property ,
170121 PersistentClass targetEntity ,
171122 Property targetProperty ) {
172123 Join otherSideJoin = null ;
@@ -196,10 +147,9 @@ private void bindTargetManyToOne(
196147 copy .setValue ( manyToOne );
197148 manyToOne .addColumn ( copy );
198149 }
199- mappedByJoin .addProperty ( property );
200- }
201- else {
202- propertyHolder .addProperty ( property , inferredData .getDeclaringClass () );
150+ // The property was added to the propertyHolder eagerly to have knowledge about this property,
151+ // in order for de-duplication to kick in, but we move it to a join if necessary
152+ propertyHolder .movePropertyToJoin ( property , mappedByJoin , inferredData .getDeclaringClass () );
203153 }
204154
205155 oneToOne .setReferencedPropertyName ( mappedBy );
@@ -235,7 +185,7 @@ private Property targetProperty(OneToOne oneToOne, PersistentClass targetEntity)
235185 + "' which does not exist in the target entity type '" + oneToOne .getReferencedEntityName () + "'" );
236186 }
237187
238- private void bindOwned (Map <String , PersistentClass > persistentClasses , OneToOne oneToOne , String propertyName , Property property ) {
188+ private void bindOwned (Map <String , PersistentClass > persistentClasses , OneToOne oneToOne , String propertyName ) {
239189 final ToOneFkSecondPass secondPass = new ToOneFkSecondPass (
240190 oneToOne ,
241191 joinColumns ,
@@ -246,7 +196,6 @@ private void bindOwned(Map<String, PersistentClass> persistentClasses, OneToOne
246196 );
247197 secondPass .doSecondPass (persistentClasses );
248198 //no column associated since it's a one to one
249- propertyHolder .addProperty ( property , inferredData .getDeclaringClass () );
250199 }
251200
252201 /**
0 commit comments