Hibernate CompositeType attribute mapping fails

Hi,

I’m upgrading my application from Hibernate 5 to 6.4.4 with Java 17. Migrated my composite user type to use new CompositeType implementation.

Mappings:

@MappedSuperclass
public abstract class PersistentEntity {
    @Id
    @GeneratedValue(strategy= GenerationType.TABLE)
    private Long id;

    public Long getId() {
        return id;
    }

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

    protected Object clone() throws CloneNotSupportedException {
        PersistentEntity clone = PersistentEntity.class.cast(super.clone());
        clone.id = null;
        return clone;
    }
}
@MappedSuperclass
public abstract class TemporalWrapper <V> extends PersistentEntity {
    @Embedded
    @AttributeOverride(name = "firstName", column = @Column(name = "firstName"))
    @AttributeOverride(name = "lastName", column = @Column(name = "lastName"))
    @CompositeType(NameCompositeUserType.class)
    public Name name;

    public Name getName() {
        return name;
    }

    public void setName(Name name) {
        this.name = name;
    }

    protected TemporalWrapper() {
    }

    public TemporalWrapper(V value){
        setValue(value);
    }
    protected abstract void setValue(V value);
}
@MappedSuperclass
public class Temporal<T>  extends TemporalWrapper<T> {
    @Embedded
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    protected Temporal() {}
    protected Temporal(T state) {
        super(state);
    }

    public boolean equals(Object obj) {
        Temporal<T> other = (Temporal<T>)obj;
        if (other == null) return false;
        return this.getId() == other.getId();
    }
}

My entity class

@Entity
@Table(name = "User")
public class User extends PersistentEntity {
    @Entity
    @Table(name = "UserTemporal")
    public static class Address extends Temporal<AddressRelation> {
        public Address() {}
        public Address(User.AddressRelation value) {
            super(value);
        }
    }

    @Embeddable
    public static class AddressRelation {
        @ManyToOne
        @Cascade(value = { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE })
        @JoinColumn(name = "objektId")
        public User objekt;

        public AddressRelation() {}

        public User getUser() {
            return objekt;
        }
        public void setUser(User objekt) {
            this.objekt = objekt;
        }
    }
}

Composite class:

public class Name implements Serializable {
    private final String first;
    private final String last;

    public Name(String first, String last) {
        this.first = first;
        this.last = last;
    }

    public String firstName() {
        return first;
    }

    public String lastName() {
        return last;
    }
}

and implementation of CompositeUserType

public class NameCompositeUserType implements CompositeUserType<Name> {
    public NameCompositeUserType() {
    }

    public static class NameMapper {
        String firstName;
        String lastName;
    }

    @Override
    public Class<?> embeddable() {
        return NameMapper.class;
    }

    @Override
    public Class<Name> returnedClass() {
        return Name.class;
    }

    @Override
    public Name instantiate(ValueAccess valueAccess, SessionFactoryImplementor sessionFactory) {
        final String first = valueAccess.getValue( 0, String.class );
        final String last = valueAccess.getValue( 1, String.class );
        return new Name(first, last);
    }

    @Override
    public Object getPropertyValue(Name component, int property) throws HibernateException {
        switch ( property ) {
            case 0:
                return component.firstName();
            case 1:
                return component.lastName();
        }
        return null;
    }

    @Override
    public boolean equals(Name x, Name y) {
        return x == y || x != null && Objects.equals( x.firstName(), y.firstName() )
                && Objects.equals( x.lastName(), y.lastName() );
    }

    @Override
    public int hashCode(Name x) {
        return Objects.hash( x.firstName(), x.lastName() );
    }

    @Override
    public Name deepCopy(Name value) {
        return value;
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Serializable disassemble(Name value) {
        return new String[] { value.firstName(), value.lastName() };
    }

    @Override
    public Name assemble(Serializable cached, Object owner) {
        final String[] parts = (String[]) cached;
        return new Name( parts[0], parts[1] );
    }

    @Override
    public Name replace(Name detached, Name managed, Object owner) {
        return detached;
    }
}

During the startup I’m getting such an error:

Caused by: org.hibernate.InstantiationException: Could not instantiate managed bean directly 'org.hibernate.bugs.Name' due to: org.hibernate.bugs.Name.<init>()
	at org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer.produceBeanInstance(FallbackBeanInstanceProducer.java:46)
	at org.hibernate.mapping.Component.createCompositeUserType(Component.java:345)
	at org.hibernate.mapping.Component.getType(Component.java:373)
	at org.hibernate.mapping.Component.getType(Component.java:66)
	at org.hibernate.metamodel.internal.AttributeFactory.determineAttributeMetadata(AttributeFactory.java:404)
	at org.hibernate.metamodel.internal.AttributeFactory.buildAttribute(AttributeFactory.java:112)
	at org.hibernate.metamodel.internal.AttributeFactory.buildAttribute(AttributeFactory.java:98)
	at org.hibernate.metamodel.internal.MetadataContext.buildAttribute(MetadataContext.java:278)
	at org.hibernate.metamodel.internal.MetadataContext.wrapUp(MetadataContext.java:362)
	at org.hibernate.metamodel.model.domain.internal.JpaMetamodelImpl.processJpa(JpaMetamodelImpl.java:524)
	at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.finishInitialization(MappingMetamodelImpl.java:214)
	at org.hibernate.internal.SessionFactoryImpl.initializeMappingModel(SessionFactoryImpl.java:364)

My question is whether is this mapping is correct?
For some reason hibernate tries to create an instance of class that represents property and cast to CompositeUserType instead of to do it with implementation NameCompositeUserType that I provided.

I created a reproducer based on your test case template.

Regards,
ToD

The error suggests that there could be a bug. Please create a Jira ticket for this, attach the reproducer and post the link to the Jira here for reference.

@beikov Thanks for a prompt reply.
The Jira ticket HHH-17916

1 Like