Another CompositeUserType question

I’m sure I’m doing something obviously wrong, but I’m not sure what so any help appreciated. I have a very simple case of CompositeUserType.

An entity…

@Entity
@Table(name = "parent_entity")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class ParentEntity {

  private Long id;
  private String color;
  private TargetEmbedded child;

  public ParentEntity () {

  }

  public ParentEntity (String color, TargetEmbedded child) {

    this.color = color;
    this.child = child;
  }

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id", unique = true, nullable = false, updatable = false)
  public synchronized Long getId () {

    return id;
  }

  public synchronized void setId (Long id) {

    this.id = id;
  }

  @Column(name = "color", length = 25, updatable = false, nullable = false)
  public String getColor () {

    return color;
  }

  public void setColor (String color) {

    this.color = color;
  }

  @Embedded
  @AttributeOverride(name = "a", column = @Column(name = "a", nullable = false))
  @AttributeOverride(name = "b", column = @Column(name = "b", nullable = false))
  @AttributeOverride(name = "c", column = @Column(name = "c", nullable = false))
  @CompositeType(TargetEmbeddedCompositeUerType.class)
  public TargetEmbedded getChild () {

    return child;
  }

  public void setChild (TargetEmbedded child) {

    this.child = child;
  }
}

An embedded to be wrapped in a user type…

public class TargetEmbedded {

  private String encoded;

  public TargetEmbedded () {

  }

  public TargetEmbedded (String encoded) {

    this.encoded = encoded;
  }

  public String getEncoded () {

    return encoded;
  }

  public void setEncoded (String encoded) {

    this.encoded = encoded;
  }
}

The user type…

public class TargetEmbeddedCompositeUerType implements CompositeUserType<TargetEmbedded> {

  @Override
  public boolean isMutable () {

    return false;
  }

  @Override
  public Class<?> embeddable () {

    return TargetedEmbeddedMapper.class;
  }

  @Override
  public Class<TargetEmbedded> returnedClass () {

    return TargetEmbedded.class;
  }

  @Override
  public boolean equals (TargetEmbedded x, TargetEmbedded y) {

    return Objects.equals(x, y);
  }

  @Override
  public int hashCode (TargetEmbedded x) {

    return Objects.hashCode(x);
  }

  @Override
  public TargetEmbedded deepCopy (TargetEmbedded value) {

    return value;
  }

  @Override
  public Serializable disassemble (TargetEmbedded value) {

    return (Serializable)value;
  }

  @Override
  public TargetEmbedded assemble (Serializable cached, Object owner) {

    return (TargetEmbedded)cached;
  }

  @Override
  public TargetEmbedded replace (TargetEmbedded detached, TargetEmbedded managed, Object owner) {

    return detached;
  }

  @Override
  public Object getPropertyValue (TargetEmbedded component, int property)
    throws HibernateException {

    if (component == null) {

      return null;
    } else {

      String[] abc = component.getEncoded().split("-", -1);

      // alphabetical
      return switch (property) {
        case 0 -> abc[0];
        case 1 -> abc[1];
        case 2 -> abc[2];
        default -> null;
      };
    }
  }

  @Override
  public TargetEmbedded instantiate (ValueAccess valueAccess, SessionFactoryImplementor sessionFactory)
    throws HibernateException {

    Object[] values = valueAccess.getValues();

    if (values.length == 3) {

      Integer a = (Integer)values[0];
      Integer b = (Integer)values[0];
      Integer c = (Integer)values[0];

      return new TargetEmbedded(((a == null) ? "0" : a.toString()) + "-" + ((b == null) ? "0" : b.toString()) + "-" + ((c == null) ? "0" : c.toString()));
    }

    return null;
  }

  public static class TargetedEmbeddedMapper {

    private Integer a;
    private Integer b;
    private Integer c;
  }
}

The specifics don’t really matter except that we have fields in the database row that need to be wrapped and unwrapped into some other type defined in the entity. My problems is that on a simple initial insert…

  @Test
  public void hhhTest123 () {

    EntityManager entityManager = entityManagerFactory.createEntityManager();
    EntityTransaction transaction = entityManager.getTransaction();

    transaction.begin();

    entityManager.merge(new ParentEntity("purple", new TargetEmbedded("1-2-3")));

    entityManager.flush();
    transaction.commit();
    entityManager.close();
  }

…Hibernate 6, specifically…

    <dependency>
      <groupId>org.hibernate.orm</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>6.4.4.Final</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate.common</groupId>
      <artifactId>hibernate-commons-annotations</artifactId>
      <version>6.0.6.Final</version>
    </dependency>

…produces this sql…

Hibernate: 
    insert 
    into
        parent_entity
        (color) 
    values
        (?)

…which ignores the embedded type entirely, and neither getPropertyVaalue() nor instatiate() is ever called, the embedded type’s fields are not added to the insert, and of course it errors.

What have I missed? How do I get that insert to include the fields from the TargetEmbedded?

From what I can see, what you’re doing is correct. This could be a bug.
Please try to create a reproducer with our test case template and if you are able to reproduce the issue, create a bug ticket in our issue tracker and attach that reproducer.

Reproductions is pretty easy. For anyone reading this who’s interested…

https://hibernate.atlassian.net/browse/HHH-17880