HHH-13228 - The modification of a @OneToOne with @MapsId property is silently ignored during a merge operation

This commit is contained in:
Vlad Mihalcea 2019-01-24 15:21:37 +02:00
parent 462e171ee3
commit 072d8ca315
3 changed files with 146 additions and 0 deletions

View File

@ -1848,4 +1848,18 @@ public interface CoreMessageLogger extends BasicLogger {
@LogMessage(level = WARN)
@Message(value = "Using @AttributeOverride or @AttributeOverrides in conjunction with entity inheritance is not supported: %s. The overriding definitions are ignored.", id = 499)
void unsupportedAttributeOverrideWithEntityInheritance(String entityName);
/** 6.0 message loggers
@LogMessage(level = WARN)
@Message(value = "The bytecode provider class [%s] could not be loaded", id = 500)
void bytecodeProviderClassNotFound(String className);
@LogMessage(level = WARN)
@Message(value = "The bytecode provider class [%s] does not implement BytecodeProvider", id = 501)
void bytecodeProviderInvalidClass(String className);
*/
@LogMessage(level = WARN)
@Message(value = "The [%s] property of the [%s] entity was modified, but it won't be updated because the property is immutable.", id = 502)
void ignoreImmutablePropertyModification(String propertyName, String entityName);
}

View File

@ -493,6 +493,10 @@ public abstract class AbstractEntityPersister
int table = propertyTableNumbers[property];
tableUpdateNeeded[table] = tableUpdateNeeded[table] ||
( getPropertyColumnSpan( property ) > 0 && updateability[property] );
if ( getPropertyColumnSpan( property ) > 0 && !updateability[property] ) {
LOG.ignoreImmutablePropertyModification( getPropertyNames()[property], getEntityName() );
}
}
if ( isVersioned() ) {
tableUpdateNeeded[0] = tableUpdateNeeded[0] ||

View File

@ -0,0 +1,128 @@
/*
* 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.annotations.onetoone;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.MapsId;
import javax.persistence.OneToOne;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.logger.LoggerInspectionRule;
import org.hibernate.testing.logger.Triggerable;
import org.junit.Rule;
import org.junit.Test;
import org.jboss.logging.Logger;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Vlad Mihalcea
*/
@TestForIssue( jiraKey = "HHH-13228" )
public class OneToOneMapsIdChangeParentTest extends BaseEntityManagerFunctionalTestCase {
@Rule
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
Logger.getMessageLogger(
CoreMessageLogger.class,
AbstractEntityPersister.class.getName()
)
);
private Triggerable triggerable = logInspection.watchForLogMessages( "HHH000502:" );
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Parent.class,
Child.class
};
}
@Test
public void test() {
Child _child = doInJPA( this::entityManagerFactory, entityManager -> {
Parent firstParent = new Parent();
firstParent.setId( 1L );
entityManager.persist(firstParent);
Child child = new Child();
child.setParent(firstParent);
entityManager.persist( child );
return child;
} );
triggerable.reset();
assertFalse( triggerable.wasTriggered() );
doInJPA( this::entityManagerFactory, entityManager -> {
Parent secondParent = new Parent();
secondParent.setId( 2L );
entityManager.persist(secondParent);
_child.setParent(secondParent);
entityManager.merge( _child );
} );
assertTrue( triggerable.wasTriggered() );
}
@Entity(name = "Parent")
public static class Parent {
@Id
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
@Entity(name = "Child")
public static class Child {
@Id
private Long id;
@OneToOne
@MapsId
private Parent parent;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
}