[JIRA] (BVAL-788) NPE when using CompositeUserType with generic fields in Hibernate 6
by Adam Pawlak (JIRA)
Adam Pawlak ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=712020%... ) *created* an issue
Bean Validation ( https://hibernate.atlassian.net/browse/BVAL?atlOrigin=eyJpIjoiZWU5MmI5MmM... ) / Bug ( https://hibernate.atlassian.net/browse/BVAL-788?atlOrigin=eyJpIjoiZWU5MmI... ) BVAL-788 ( https://hibernate.atlassian.net/browse/BVAL-788?atlOrigin=eyJpIjoiZWU5MmI... ) NPE when using CompositeUserType with generic fields in Hibernate 6 ( https://hibernate.atlassian.net/browse/BVAL-788?atlOrigin=eyJpIjoiZWU5MmI... )
Issue Type: Bug Assignee: Unassigned Created: 14/Aug/2023 07:49 AM Environment: hibernate 6.2.6 Priority: Major Reporter: Adam Pawlak ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=712020%... )
After migrating from hiberante 5.1.2 to hibernate 6.2.6 as i understand it, i must used CompositeUserType (instead of userType) to map 2 columns to one object. It’s all working great BUT if this object contains generic fields then i get NPE:
Caused by: java.lang.NullPointerException: Cannot invoke "[I.clone()" because "original.originalPropertyOrder" is null
at org.hibernate.mapping.Component.<init>(Component.java:121) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.mapping.Component.copy(Component.java:137) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.ClassPropertyHolder.handleGenericComponentProperty(ClassPropertyHolder.java:246) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.ClassPropertyHolder.addPropertyToPersistentClass(ClassPropertyHolder.java:264) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.ClassPropertyHolder.addProperty(ClassPropertyHolder.java:217) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.ClassPropertyHolder.addProperty(ClassPropertyHolder.java:203) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.PropertyBinder.bind(PropertyBinder.java:327) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.PropertyBinder.makePropertyAndBind(PropertyBinder.java:288) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.EmbeddableBinder.createEmbeddedProperty(EmbeddableBinder.java:245) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.EmbeddableBinder.createCompositeBinder(EmbeddableBinder.java:118) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.PropertyBinder.bindBasic(PropertyBinder.java:1022) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.PropertyBinder.bindProperty(PropertyBinder.java:867) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.PropertyBinder.buildProperty(PropertyBinder.java:765) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations(PropertyBinder.java:686) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.EntityBinder.processIdPropertiesIfNotAlready(EntityBinder.java:967) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.EntityBinder.handleIdentifier(EntityBinder.java:302) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.EntityBinder.bindEntityClass(EntityBinder.java:228) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.internal.AnnotationBinder.bindClass(AnnotationBinder.java:417) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:255) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:275) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:318) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1380) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1451) ~[hibernate-core-6.2.6.Final.jar:6.2.6.Final]
at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75) ~[spring-orm-6.0.11.jar:6.0.11]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.0.11.jar:6.0.11]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.0.11.jar:6.0.11]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.0.11.jar:6.0.11]
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.0.11.jar:6.0.11]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1817) ~[spring-beans-6.0.11.jar:6.0.11]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1766) ~[spring-beans-6.0.11.jar:6.0.11]
Sample code:
Entity:
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import org.hibernate.annotations.CompositeType;
import pl.org.opi.polon2.employees.shared.IntermediateEventUserTypeBoo;
@Entity
public class BooEntity {
@Id
private Long id;
@Lob
@Embedded
@CompositeType(value = IntermediateEventUserTypeBoo.class)
@AttributeOverrides({
@AttributeOverride(name = "type", column = @Column(name = "TYPE", updatable = false)),
@AttributeOverride(name = "jsonValue", column = @Column(name = "DATA", updatable = false, columnDefinition = "clob"))
})
protected Boo boo;
public BooEntity(Boo boo) {
this.boo = boo;
}
}
IntermediateEventUserTypeBoo :
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.spi.ValueAccess;
import org.hibernate.usertype.CompositeUserType;
import pl.org.opi.polon2.employees.employment.Boo;
import java.io.Serializable;
import java.util.Objects;
public class IntermediateEventUserTypeBoo implements CompositeUserType<Boo> {
public static class EmbeddableMapper {
String type;
String jsonValue;
}
@Override
public Object getPropertyValue(Boo component, int property) throws HibernateException {
return switch (property) {
case 0 -> component.getType();
case 1 -> component.getJson();
default -> new RuntimeException();
};
}
@Override
public Boo instantiate(ValueAccess values, SessionFactoryImplementor sessionFactory) {
String type = values.getValue(0, String.class);
BooEnum booEnum = Enum.valueOf(BooEnum.class, type);
String json = values.getValue(1, String.class);
return new Boo(booEnum, json);
}
@Override
public Class<?> embeddable() {
return EmbeddableMapper.class;
}
@Override
public Class<Boo> returnedClass() {
return Boo.class;
}
@Override
public boolean equals(Boo x, Boo y) {
return Objects.equals(x, y);
}
@Override
public int hashCode(Boo x) {
return Objects.hashCode(x);
}
@Override
public Boo deepCopy(Boo value) {
return new Boo<>(value.getType(), value.getJson());
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(Boo value) {
return (Serializable) value;
}
@Override
public Boo assemble(Serializable cached, Object owner) {
return (Boo) cached;
}
@Override
public Boo replace(Boo detached, Boo managed, Object owner) {
return detached;
}
}
BooEnum:
public enum BooEnum {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
SATURDAY,
SUNDAY
}
Boo:
public class Boo<T extends Enum<T>> {
private final T type;
private String json;
public Boo(T type, String revision) {
this.type = type;
this.json = revision;
}
public T getType() {
return type;
}
public String getJson() {
return json;
}
}
BooTest:
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import pl.org.opi.polon2.employees.employment.Boo;
import pl.org.opi.polon2.employees.employment.BooEntity;
import pl.org.opi.polon2.employees.shared.BooEnum;
public class BooTest {
private EntityManagerFactory entityManagerFactory;
@Before
public void init() {
entityManagerFactory = Persistence.createEntityManagerFactory( "templatePU" );
}
@After
public void destroy() {
entityManagerFactory.close();
}
// Entities are auto-discovered, so just add them anywhere on class-path
// Add your tests, using standard JUnit.
@Test
public void hhh123Test() throws Exception {
EntityManager entityManager = entityManagerFactory.createEntityManager();
entityManager.getTransaction().begin();
Boo<BooEnum> booEnumBoo = new Boo<>(BooEnum.SATURDAY, "2");
BooEntity booEntity = new BooEntity(booEnumBoo);
entityManager.getTransaction().commit();
entityManager.close();
}
}
In my opinion problem is that the class Boo contains generic field. When i change class Boo on:
public class Boo {
private final String type;
private String json;
public Boo(String type, String revision) {
this.type = type;
this.json = revision;
}
public String getType() {
return type;
}
public String getJson() {
return json;
}
}
Everything is good
( https://hibernate.atlassian.net/browse/BVAL-788#add-comment?atlOrigin=eyJ... ) Add Comment ( https://hibernate.atlassian.net/browse/BVAL-788#add-comment?atlOrigin=eyJ... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100234- sha1:d099d86 )
1 year, 6 months