Skip to content

HHH-12436 - Attempted to assign id from null one-to-one property #2226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UnionSubclass;
import org.hibernate.type.ForeignKeyDirection;

import static org.hibernate.internal.CoreLogging.messageLogger;

Expand Down Expand Up @@ -3135,6 +3136,10 @@ private static void bindOneToOne(
}
}
}
ForeignKeyDirection foreignKeyDirection = !BinderHelper.isEmptyAnnotationValue( mappedBy )
? ForeignKeyDirection.TO_PARENT
: ForeignKeyDirection.FROM_PARENT;

if ( trueOneToOne || mapToPK || !BinderHelper.isEmptyAnnotationValue( mappedBy ) ) {
//is a true one-to-one
//FIXME referencedColumnName ignored => ordering may fail.
Expand All @@ -3150,7 +3155,8 @@ private static void bindOneToOne(
optional,
cascadeStrategy,
joinColumns,
context
context,
foreignKeyDirection
);
if ( inSecondPass ) {
secondPass.doSecondPass( context.getMetadataCollector().getEntityBindingMap() );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public class OneToOneSecondPass implements SecondPass {
private boolean optional;
private String cascadeStrategy;
private Ejb3JoinColumn[] joinColumns;
private ForeignKeyDirection foreignKeyDirection;

//that suck, we should read that from the property mainly
public OneToOneSecondPass(
Expand All @@ -60,7 +61,8 @@ public OneToOneSecondPass(
boolean optional,
String cascadeStrategy,
Ejb3JoinColumn[] columns,
MetadataBuildingContext buildingContext) {
MetadataBuildingContext buildingContext,
ForeignKeyDirection foreignKeyDirection) {
this.ownerEntity = ownerEntity;
this.ownerProperty = ownerProperty;
this.mappedBy = mappedBy;
Expand All @@ -73,6 +75,7 @@ public OneToOneSecondPass(
this.optional = optional;
this.cascadeStrategy = cascadeStrategy;
this.joinColumns = columns;
this.foreignKeyDirection = foreignKeyDirection;
}

//TODO refactor this code, there is a lot of duplication in this method
Expand All @@ -94,16 +97,8 @@ public void doSecondPass(Map persistentClasses) throws MappingException {
if ( !optional ) {
value.setConstrained( true );
}
if ( value.isReferenceToPrimaryKey() ) {
value.setForeignKeyType( ForeignKeyDirection.TO_PARENT );
}
else {
value.setForeignKeyType(
value.isConstrained()
? ForeignKeyDirection.FROM_PARENT
: ForeignKeyDirection.TO_PARENT
);
}
value.setForeignKeyType(foreignKeyDirection);

PropertyBinder binder = new PropertyBinder();
binder.setName( propertyName );
binder.setValue( value );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ public Type getType() throws MappingException {
isLazy(),
isUnwrapProxy(),
entityName,
propertyName
propertyName,
constrained
);
}
else {
Expand All @@ -83,7 +84,8 @@ public Type getType() throws MappingException {
isLazy(),
isUnwrapProxy(),
entityName,
propertyName
propertyName,
constrained
);
}
}
Expand Down
15 changes: 10 additions & 5 deletions hibernate-core/src/main/java/org/hibernate/type/OneToOneType.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ public class OneToOneType extends EntityType {
private final ForeignKeyDirection foreignKeyType;
private final String propertyName;
private final String entityName;
private final boolean constrained;

/**
* @deprecated Use {@link #OneToOneType(TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String)}
* @deprecated Use {@link #OneToOneType(TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String, boolean)}
* instead.
*/
@Deprecated
Expand All @@ -43,8 +44,9 @@ public OneToOneType(
boolean lazy,
boolean unwrapProxy,
String entityName,
String propertyName) {
this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName );
String propertyName,
boolean constrained) {
this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName, constrained );
}

public OneToOneType(
Expand All @@ -56,18 +58,21 @@ public OneToOneType(
boolean lazy,
boolean unwrapProxy,
String entityName,
String propertyName) {
String propertyName,
boolean constrained) {
super( scope, referencedEntityName, referenceToPrimaryKey, uniqueKeyPropertyName, !lazy, unwrapProxy );
this.foreignKeyType = foreignKeyType;
this.propertyName = propertyName;
this.entityName = entityName;
this.constrained = constrained;
}

public OneToOneType(OneToOneType original, String superTypeEntityName) {
super( original, superTypeEntityName );
this.foreignKeyType = original.foreignKeyType;
this.propertyName = original.propertyName;
this.entityName = original.entityName;
this.constrained = original.constrained;
}

@Override
Expand Down Expand Up @@ -156,7 +161,7 @@ public Object hydrate(

@Override
protected boolean isNullable() {
return foreignKeyType==ForeignKeyDirection.TO_PARENT;
return !constrained;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
public class SpecialOneToOneType extends OneToOneType {

/**
* @deprecated Use {@link #SpecialOneToOneType(org.hibernate.type.TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String)} instead.
* @deprecated Use {@link #SpecialOneToOneType(org.hibernate.type.TypeFactory.TypeScope, String, ForeignKeyDirection, boolean, String, boolean, boolean, String, String, boolean)} instead.
*/
@Deprecated
public SpecialOneToOneType(
Expand All @@ -38,8 +38,9 @@ public SpecialOneToOneType(
boolean lazy,
boolean unwrapProxy,
String entityName,
String propertyName) {
this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName );
String propertyName,
boolean constrained) {
this( scope, referencedEntityName, foreignKeyType, uniqueKeyPropertyName == null, uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName, constrained );
}

public SpecialOneToOneType(
Expand All @@ -51,7 +52,8 @@ public SpecialOneToOneType(
boolean lazy,
boolean unwrapProxy,
String entityName,
String propertyName) {
String propertyName,
boolean constrained) {
super(
scope,
referencedEntityName,
Expand All @@ -61,7 +63,8 @@ public SpecialOneToOneType(
lazy,
unwrapProxy,
entityName,
propertyName
propertyName,
constrained
);
}

Expand Down
10 changes: 6 additions & 4 deletions hibernate-core/src/main/java/org/hibernate/type/TypeFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,11 @@ public EntityType oneToOne(
boolean lazy,
boolean unwrapProxy,
String entityName,
String propertyName) {
String propertyName,
boolean constrained) {
return new OneToOneType(
typeScope, persistentClass, foreignKeyType, referenceToPrimaryKey,
uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName
uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName, constrained
);
}

Expand All @@ -223,10 +224,11 @@ public EntityType specialOneToOne(
boolean lazy,
boolean unwrapProxy,
String entityName,
String propertyName) {
String propertyName,
boolean constrained) {
return new SpecialOneToOneType(
typeScope, persistentClass, foreignKeyType, referenceToPrimaryKey,
uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName
uniqueKeyPropertyName, lazy, unwrapProxy, entityName, propertyName, constrained
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.test.ops;

import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.MapsId;
import javax.persistence.OneToOne;

import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;

import org.hibernate.testing.TestForIssue;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertNotNull;

/**
*
* @author localEvg
*/
@TestForIssue( jiraKey = "HHH-12436" )
public class OneToOneMergeTest extends BaseEntityManagerFunctionalTestCase {

@Override
public Class<?>[] getAnnotatedClasses() {
return new Class[]{
Prima.class,
Secunda.class
};
}

@Test
public void testMerge() throws Exception {

Long primaId = doInJPA( this::entityManagerFactory, entityManager -> {
Prima prima = new Prima();
prima.setOptionalData(null);

entityManager.persist(prima);

return prima.getId();
} );

assertNotNull(primaId);

doInJPA( this::entityManagerFactory, entityManager -> {
Prima prima = entityManager.find( Prima.class, primaId );

Secunda sec = new Secunda();
sec.setParent(prima);
prima.setOptionalData(sec);

Prima mergedPrima = entityManager.merge(prima);

assertNotNull(mergedPrima);
} );

}

@Entity(name = "Prima")
public static class Prima implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

//@PrimaryKeyJoinColumn
@OneToOne(mappedBy = "parent", optional = true , cascade = CascadeType.ALL)
private Secunda optionalData;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Secunda getOptionalData() {
return optionalData;
}

public void setOptionalData(Secunda optionalData) {
this.optionalData = optionalData;
}

}

@Entity(name = "Secunda")
public static class Secunda implements Serializable {

@Id
@Column(name = "id", nullable = false)
private Long id;

@MapsId
@OneToOne(optional = false)
@JoinColumn(name = "id", nullable = false)
private Prima parent;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public Prima getParent() {
return parent;
}

public void setParent(Prima parent) {
this.parent = parent;
}

}
}