HHH-14360 Test a fix for NPE due to non-existing previous state in bytecode enhanced dirty attribute resolving
This commit is contained in:
parent
dbec10aff1
commit
c915168cf1
|
@ -2280,7 +2280,8 @@ public abstract class AbstractEntityPersister
|
|||
mutablePropertiesIndexes.stream().forEach( i -> {
|
||||
// This is kindly borrowed from org.hibernate.type.TypeHelper.findDirty
|
||||
final boolean dirty = currentState[i] != LazyPropertyInitializer.UNFETCHED_PROPERTY &&
|
||||
( previousState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ||
|
||||
// Consider mutable properties as dirty if we don't have a previous state
|
||||
( previousState == null || previousState[i] == LazyPropertyInitializer.UNFETCHED_PROPERTY ||
|
||||
( propertyCheckability[i]
|
||||
&& propertyTypes[i].isDirty(
|
||||
previousState[i],
|
||||
|
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.dirty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Basic;
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderBy;
|
||||
import javax.persistence.OrderColumn;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.Temporal;
|
||||
import javax.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.boot.internal.SessionFactoryBuilderImpl;
|
||||
import org.hibernate.boot.internal.SessionFactoryOptionsBuilder;
|
||||
import org.hibernate.boot.spi.SessionFactoryBuilderService;
|
||||
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.hibernate.testing.transaction.TransactionUtil;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-14360")
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
public class DirtyTrackingPersistTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
public Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { HotherEntity.class, Hentity.class };
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(Configuration configuration) {
|
||||
super.configure( configuration );
|
||||
configuration.getStandardServiceRegistryBuilder().addService(
|
||||
SessionFactoryBuilderService.class,
|
||||
(SessionFactoryBuilderService) (metadata, bootstrapContext) -> {
|
||||
SessionFactoryOptionsBuilder optionsBuilder = new SessionFactoryOptionsBuilder(
|
||||
metadata.getMetadataBuildingOptions().getServiceRegistry(),
|
||||
bootstrapContext
|
||||
);
|
||||
optionsBuilder.enableCollectionInDefaultFetchGroup( true );
|
||||
return new SessionFactoryBuilderImpl( metadata, optionsBuilder );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Hentity hentity = new Hentity();
|
||||
HotherEntity hotherEntity = new HotherEntity();
|
||||
hentity.setLineItems( new ArrayList<>( Collections.singletonList( hotherEntity ) ) );
|
||||
hentity.setNextRevUNs( new ArrayList<>( Collections.singletonList( "something" ) ) );
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.persist( hentity );
|
||||
} );
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
hentity.bumpNumber();
|
||||
session.saveOrUpdate( hentity );
|
||||
} );
|
||||
}
|
||||
|
||||
// --- //
|
||||
|
||||
@Entity
|
||||
public static class HotherEntity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
@Basic
|
||||
private Long clicId;
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public Long getClicId() {
|
||||
return clicId;
|
||||
}
|
||||
|
||||
public void setClicId(Long clicId) {
|
||||
this.clicId = clicId;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
public static class Hentity {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ElementCollection
|
||||
@OrderColumn(name = "nextRevUN_index")
|
||||
private List<String> nextRevUNs;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
|
||||
@JoinColumn(name = "clicId")
|
||||
@OrderBy("id asc")
|
||||
protected List<HotherEntity> lineItems;
|
||||
|
||||
@Basic
|
||||
private Long number;
|
||||
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date createDate;
|
||||
|
||||
@Temporal(value = TemporalType.TIMESTAMP)
|
||||
private Date deleteDate;
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<String> getNextRevUNs() {
|
||||
return nextRevUNs;
|
||||
}
|
||||
|
||||
public void setNextRevUNs(List<String> nextRevUNs) {
|
||||
this.nextRevUNs = nextRevUNs;
|
||||
}
|
||||
|
||||
public List<HotherEntity> getLineItems() {
|
||||
return lineItems;
|
||||
}
|
||||
|
||||
public void setLineItems(List<HotherEntity> lineItems) {
|
||||
this.lineItems = lineItems;
|
||||
}
|
||||
|
||||
public Date getCreateDate() {
|
||||
return createDate;
|
||||
}
|
||||
|
||||
public void setCreateDate(Date createDate) {
|
||||
this.createDate = createDate;
|
||||
}
|
||||
|
||||
public Date getDeleteDate() {
|
||||
return deleteDate;
|
||||
}
|
||||
|
||||
public void setDeleteDate(Date deleteDate) {
|
||||
this.deleteDate = deleteDate;
|
||||
}
|
||||
|
||||
public void bumpNumber() {
|
||||
number = number == null ? 0 : number++;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue