Skip to content

Commit e361ad4

Browse files
committed
HHH-6813 @id @OnetoOne cause NullPointerException during query
1 parent c1eff0f commit e361ad4

File tree

14 files changed

+204
-75
lines changed

14 files changed

+204
-75
lines changed

hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,7 @@ public static void createSyntheticPropertyReference(
341341
*/
342342
if ( value instanceof ToOne ) {
343343
( (ToOne) value ).setReferencedPropertyName( syntheticPropertyName );
344+
( (ToOne) value ).setReferenceToPrimaryKey( syntheticPropertyName == null );
344345
mappings.addUniquePropertyReference( ownerEntity.getEntityName(), syntheticPropertyName );
345346
}
346347
else if ( value instanceof Collection ) {

hibernate-core/src/main/java/org/hibernate/cfg/HbmBinder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,6 +1629,7 @@ public static void bindManyToOne(Element node, ManyToOne manyToOne, String path,
16291629
if ( ukName != null ) {
16301630
manyToOne.setReferencedPropertyName( ukName.getValue() );
16311631
}
1632+
manyToOne.setReferenceToPrimaryKey( manyToOne.getReferencedPropertyName() == null );
16321633

16331634
manyToOne.setReferencedEntityName( getEntityName( node, mappings ) );
16341635

@@ -1728,6 +1729,7 @@ public static void bindOneToOne(Element node, OneToOne oneToOne, String path, bo
17281729

17291730
Attribute ukName = node.attribute( "property-ref" );
17301731
if ( ukName != null ) oneToOne.setReferencedPropertyName( ukName.getValue() );
1732+
oneToOne.setReferenceToPrimaryKey( oneToOne.getReferencedPropertyName() == null );
17311733

17321734
oneToOne.setPropertyName( node.attributeValue( "name" ) );
17331735

hibernate-core/src/main/java/org/hibernate/cfg/OneToOneSecondPass.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.hibernate.mapping.OneToOne;
4040
import org.hibernate.mapping.PersistentClass;
4141
import org.hibernate.mapping.Property;
42+
import org.hibernate.mapping.Selectable;
4243
import org.hibernate.mapping.SimpleValue;
4344
import org.hibernate.type.ForeignKeyDirection;
4445

@@ -212,12 +213,10 @@ else if ( otherSideProperty.getValue() instanceof ManyToOne ) {
212213
else {
213214
propertyHolder.addProperty( prop, inferredData.getDeclaringClass() );
214215
}
216+
217+
value.setReferencedPropertyName( mappedBy );
215218

216219
// HHH-6813
217-
// If otherSide's id is derived, do not set EntityType#uniqueKeyPropertyName.
218-
// EntityType#isReferenceToPrimaryKey() assumes that, if it's set,
219-
// a PK is not referenced. Example:
220-
//
221220
// Foo: @Id long id, @OneToOne(mappedBy="foo") Bar bar
222221
// Bar: @Id @OneToOne Foo foo
223222
boolean referencesDerivedId = false;
@@ -228,8 +227,14 @@ else if ( otherSideProperty.getValue() instanceof ManyToOne ) {
228227
catch ( MappingException e ) {
229228
// ignore
230229
}
231-
String referencedPropertyName = referencesDerivedId ? null : mappedBy;
232-
value.setReferencedPropertyName( referencedPropertyName );
230+
boolean referenceToPrimaryKey = referencesDerivedId || mappedBy == null;
231+
value.setReferenceToPrimaryKey( referenceToPrimaryKey );
232+
233+
// If the other side is a derived ID, prevent an infinite
234+
// loop of attempts to resolve identifiers.
235+
if ( referencesDerivedId ) {
236+
( (ManyToOne) otherSideProperty.getValue() ).setReferenceToPrimaryKey( false );
237+
}
233238

234239
String propertyRef = value.getReferencedPropertyName();
235240
if ( propertyRef != null ) {

hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1400,6 +1400,7 @@ public static void bindManytoManyInverseFk(
14001400
( (ManyToOne) value ).setReferencedPropertyName( referencedPropertyName );
14011401
mappings.addUniquePropertyReference( referencedEntity.getEntityName(), referencedPropertyName );
14021402
}
1403+
( (ManyToOne) value ).setReferenceToPrimaryKey( referencedPropertyName == null );
14031404
value.createForeignKey();
14041405
}
14051406
else {

hibernate-core/src/main/java/org/hibernate/mapping/ManyToOne.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public ManyToOne(Mappings mappings, Table table) {
4545

4646
public Type getType() throws MappingException {
4747
return getMappings().getTypeResolver().getTypeFactory().manyToOne(
48-
getReferencedEntityName(),
48+
getReferencedEntityName(),
49+
referenceToPrimaryKey,
4950
getReferencedPropertyName(),
5051
isLazy(),
5152
isUnwrapProxy(),

hibernate-core/src/main/java/org/hibernate/mapping/OneToMany.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ public class OneToMany implements Value {
4747

4848
private EntityType getEntityType() {
4949
return mappings.getTypeResolver().getTypeFactory().manyToOne(
50-
getReferencedEntityName(),
50+
getReferencedEntityName(),
51+
true,
5152
null,
5253
false,
5354
false,

hibernate-core/src/main/java/org/hibernate/mapping/OneToOne.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ public Type getType() throws MappingException {
6969
if ( getColumnIterator().hasNext() ) {
7070
return getMappings().getTypeResolver().getTypeFactory().specialOneToOne(
7171
getReferencedEntityName(),
72-
foreignKeyType,
72+
foreignKeyType,
73+
referenceToPrimaryKey,
7374
referencedPropertyName,
7475
isLazy(),
7576
isUnwrapProxy(),
@@ -80,7 +81,8 @@ public Type getType() throws MappingException {
8081
else {
8182
return getMappings().getTypeResolver().getTypeFactory().oneToOne(
8283
getReferencedEntityName(),
83-
foreignKeyType,
84+
foreignKeyType,
85+
referenceToPrimaryKey,
8486
referencedPropertyName,
8587
isLazy(),
8688
isUnwrapProxy(),

hibernate-core/src/main/java/org/hibernate/mapping/ToOne.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public abstract class ToOne extends SimpleValue implements Fetchable {
4141
private boolean embedded;
4242
private boolean lazy = true;
4343
protected boolean unwrapProxy;
44+
protected boolean referenceToPrimaryKey = true;
4445

4546
protected ToOne(Mappings mappings, Table table) {
4647
super( mappings, table );
@@ -129,5 +130,13 @@ public boolean isUnwrapProxy() {
129130
public void setUnwrapProxy(boolean unwrapProxy) {
130131
this.unwrapProxy = unwrapProxy;
131132
}
133+
134+
public boolean isReferenceToPrimaryKey() {
135+
return referenceToPrimaryKey;
136+
}
137+
138+
public void setReferenceToPrimaryKey(boolean referenceToPrimaryKey) {
139+
this.referenceToPrimaryKey = referenceToPrimaryKey;
140+
}
132141

133142
}

hibernate-core/src/main/java/org/hibernate/type/EntityType.java

100755100644
Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,30 @@
2323
*/
2424
package org.hibernate.type;
2525

26+
import java.io.Serializable;
27+
import java.sql.ResultSet;
28+
import java.sql.SQLException;
29+
import java.util.Map;
30+
2631
import org.dom4j.Element;
2732
import org.dom4j.Node;
2833
import org.hibernate.AssertionFailure;
2934
import org.hibernate.EntityMode;
3035
import org.hibernate.HibernateException;
3136
import org.hibernate.MappingException;
3237
import org.hibernate.engine.internal.ForeignKeys;
33-
import org.hibernate.engine.spi.*;
38+
import org.hibernate.engine.spi.EntityUniqueKey;
39+
import org.hibernate.engine.spi.Mapping;
40+
import org.hibernate.engine.spi.PersistenceContext;
41+
import org.hibernate.engine.spi.SessionFactoryImplementor;
42+
import org.hibernate.engine.spi.SessionImplementor;
3443
import org.hibernate.internal.util.ReflectHelper;
3544
import org.hibernate.persister.entity.EntityPersister;
3645
import org.hibernate.persister.entity.Joinable;
3746
import org.hibernate.persister.entity.UniqueKeyLoadable;
3847
import org.hibernate.proxy.HibernateProxy;
3948
import org.hibernate.tuple.ElementWrapper;
4049

41-
import java.io.Serializable;
42-
import java.sql.ResultSet;
43-
import java.sql.SQLException;
44-
import java.util.Map;
45-
4650
/**
4751
* Base for types which map associations to persistent entities.
4852
*
@@ -56,6 +60,7 @@ public abstract class EntityType extends AbstractType implements AssociationType
5660
protected final boolean isEmbeddedInXML;
5761
private final boolean eager;
5862
private final boolean unwrapProxy;
63+
private final boolean referenceToPrimaryKey;
5964

6065
private transient Class returnedClass;
6166

@@ -72,7 +77,7 @@ public abstract class EntityType extends AbstractType implements AssociationType
7277
* says to return the "implementation target" of lazy prooxies; typically only possible
7378
* with lazy="no-proxy".
7479
*
75-
* @deprecated Use {@link #EntityType(TypeFactory.TypeScope, String, String, boolean, boolean )} instead.
80+
* @deprecated Use {@link #EntityType(org.hibernate.type.TypeFactory.TypeScope, String, boolean, String, boolean, boolean)} instead.
7681
* See Jira issue: <a href="https://hibernate.onjira.com/browse/HHH-7771">HHH-7771</a>
7782
*/
7883
@Deprecated
@@ -83,12 +88,7 @@ protected EntityType(
8388
boolean eager,
8489
boolean isEmbeddedInXML,
8590
boolean unwrapProxy) {
86-
this.scope = scope;
87-
this.associatedEntityName = entityName;
88-
this.uniqueKeyPropertyName = uniqueKeyPropertyName;
89-
this.isEmbeddedInXML = isEmbeddedInXML;
90-
this.eager = eager;
91-
this.unwrapProxy = unwrapProxy;
91+
this( scope, entityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, eager, unwrapProxy );
9292
}
9393

9494
/**
@@ -102,19 +102,46 @@ protected EntityType(
102102
* @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping
103103
* says to return the "implementation target" of lazy prooxies; typically only possible
104104
* with lazy="no-proxy".
105+
*
106+
* @deprecated Use {@link #EntityType(org.hibernate.type.TypeFactory.TypeScope, String, boolean, String, boolean, boolean)} instead.
105107
*/
108+
@Deprecated
106109
protected EntityType(
107110
TypeFactory.TypeScope scope,
108111
String entityName,
109112
String uniqueKeyPropertyName,
110113
boolean eager,
111114
boolean unwrapProxy) {
115+
this( scope, entityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, eager, unwrapProxy );
116+
}
117+
118+
/**
119+
* Constructs the requested entity type mapping.
120+
*
121+
* @param scope The type scope
122+
* @param entityName The name of the associated entity.
123+
* @param referenceToPrimaryKey True if association references a primary key.
124+
* @param uniqueKeyPropertyName The property-ref name, or null if we
125+
* reference the PK of the associated entity.
126+
* @param eager Is eager fetching enabled.
127+
* @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping
128+
* says to return the "implementation target" of lazy prooxies; typically only possible
129+
* with lazy="no-proxy".
130+
*/
131+
protected EntityType(
132+
TypeFactory.TypeScope scope,
133+
String entityName,
134+
boolean referenceToPrimaryKey,
135+
String uniqueKeyPropertyName,
136+
boolean eager,
137+
boolean unwrapProxy) {
112138
this.scope = scope;
113139
this.associatedEntityName = entityName;
114140
this.uniqueKeyPropertyName = uniqueKeyPropertyName;
115141
this.isEmbeddedInXML = true;
116142
this.eager = eager;
117143
this.unwrapProxy = unwrapProxy;
144+
this.referenceToPrimaryKey = referenceToPrimaryKey;
118145
}
119146

120147
protected TypeFactory.TypeScope scope() {
@@ -169,7 +196,7 @@ public String getName() {
169196
* @return True if this association reference the PK of the associated entity.
170197
*/
171198
public boolean isReferenceToPrimaryKey() {
172-
return uniqueKeyPropertyName==null;
199+
return referenceToPrimaryKey;
173200
}
174201

175202
public String getRHSUniqueKeyPropertyName() {
@@ -456,21 +483,16 @@ public Object resolve(Object value, SessionImplementor session, Object owner) th
456483
return value;
457484
}
458485

459-
if ( value == null ) {
460-
return null;
461-
}
462-
else {
463-
if ( isNull( owner, session ) ) {
464-
return null; //EARLY EXIT!
465-
}
466-
486+
if ( value != null && !isNull( owner, session ) ) {
467487
if ( isReferenceToPrimaryKey() ) {
468488
return resolveIdentifier( (Serializable) value, session );
469489
}
470-
else {
490+
else if ( uniqueKeyPropertyName != null ) {
471491
return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session );
472492
}
473493
}
494+
495+
return null;
474496
}
475497

476498
public Type getSemiResolvedType(SessionFactoryImplementor factory) {
@@ -482,7 +504,7 @@ protected final Object getIdentifier(Object value, SessionImplementor session) t
482504
return value;
483505
}
484506

485-
if ( isReferenceToPrimaryKey() ) {
507+
if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) {
486508
return ForeignKeys.getEntityIdentifierIfNotUnsaved( getAssociatedEntityName(), value, session ); //tolerates nulls
487509
}
488510
else if ( value == null ) {
@@ -599,7 +621,7 @@ Type getIdentifierType(SessionImplementor session) {
599621
* or unique key property name.
600622
*/
601623
public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingException {
602-
if ( isReferenceToPrimaryKey() ) {
624+
if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) {
603625
return getIdentifierType(factory);
604626
}
605627
else {
@@ -621,7 +643,7 @@ public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingEx
621643
*/
622644
public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
623645
throws MappingException {
624-
if ( isReferenceToPrimaryKey() ) {
646+
if ( isReferenceToPrimaryKey() || uniqueKeyPropertyName == null ) {
625647
return factory.getIdentifierPropertyName( getAssociatedEntityName() );
626648
}
627649
else {

hibernate-core/src/main/java/org/hibernate/type/ManyToOneType.java

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,12 @@ public ManyToOneType(TypeFactory.TypeScope scope, String referencedEntityName) {
6767
* @param lazy Should the association be handled lazily
6868
*/
6969
public ManyToOneType(TypeFactory.TypeScope scope, String referencedEntityName, boolean lazy) {
70-
this( scope, referencedEntityName, null, lazy, true, false, false, false );
70+
this( scope, referencedEntityName, true, null, lazy, true, false, false );
7171
}
7272

7373

7474
/**
75-
* @deprecated Use {@link #ManyToOneType(TypeFactory.TypeScope, String, String, boolean, boolean, boolean, boolean ) } instead.
76-
* See Jira issue: <a href="https://hibernate.onjira.com/browse/HHH-7771">HHH-7771</a>
75+
* @deprecated Use {@link #ManyToOneType(TypeFactory.TypeScope, String, boolean, String, boolean, boolean, boolean, boolean ) } instead.
7776
*/
7877
@Deprecated
7978
public ManyToOneType(
@@ -85,20 +84,35 @@ public ManyToOneType(
8584
boolean isEmbeddedInXML,
8685
boolean ignoreNotFound,
8786
boolean isLogicalOneToOne) {
88-
super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, isEmbeddedInXML, unwrapProxy );
89-
this.ignoreNotFound = ignoreNotFound;
90-
this.isLogicalOneToOne = isLogicalOneToOne;
87+
this( scope, referencedEntityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne );
88+
}
89+
90+
/**
91+
* @deprecated Use {@link #ManyToOneType(TypeFactory.TypeScope, String, boolean, String, boolean, boolean, boolean, boolean ) } instead.
92+
* See Jira issue: <a href="https://hibernate.onjira.com/browse/HHH-7771">HHH-7771</a>
93+
*/
94+
@Deprecated
95+
public ManyToOneType(
96+
TypeFactory.TypeScope scope,
97+
String referencedEntityName,
98+
String uniqueKeyPropertyName,
99+
boolean lazy,
100+
boolean unwrapProxy,
101+
boolean ignoreNotFound,
102+
boolean isLogicalOneToOne) {
103+
this( scope, referencedEntityName, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, ignoreNotFound, isLogicalOneToOne );
91104
}
92105

93106
public ManyToOneType(
94107
TypeFactory.TypeScope scope,
95108
String referencedEntityName,
109+
boolean referenceToPrimaryKey,
96110
String uniqueKeyPropertyName,
97111
boolean lazy,
98112
boolean unwrapProxy,
99113
boolean ignoreNotFound,
100114
boolean isLogicalOneToOne) {
101-
super( scope, referencedEntityName, uniqueKeyPropertyName, !lazy, unwrapProxy );
115+
super( scope, referencedEntityName, referenceToPrimaryKey, uniqueKeyPropertyName, !lazy, unwrapProxy );
102116
this.ignoreNotFound = ignoreNotFound;
103117
this.isLogicalOneToOne = isLogicalOneToOne;
104118
}

0 commit comments

Comments
 (0)