Affects: \v3.0.0-M4


Thsi issue might be hibernate related, since between M3 and M4 it switched from hibernate 5.x to 6.1.1. If that is the case, I apologize in advance.

I have a mapped superclass containing an id field that is used by all my entities, and an actual entity object with the rest of the business-related fields. The id is UUID, which on the database side is mapped into a 16 character long blob. To do the conversion, I use a custom AttributeConverter. DB is initialized by Spring, based on the entities.

When I try to save such an object, I receive one of two exceptions. It is either:

class java.util.UUID cannot be cast to class [B (java.util.UUID and [B are in module java.base of loader 'bootstrap')
java.lang.ClassCastException: class java.util.UUID cannot be cast to class [B (java.util.UUID and [B are in module java.base of loader 'bootstrap')
    at org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType.areEqual(PrimitiveByteArrayJavaType.java:28)

or

org.springframework.dao.InvalidDataAccessApiUsageException: Value was not an array [java.util.UUID]
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:371)
...
Caused by: java.lang.IllegalArgumentException: Value was not an array [java.util.UUID]
    at org.hibernate.type.descriptor.java.ArrayMutabilityPlan.deepCopyNotNull(ArrayMutabilityPlan.java:23)

Full logs: v1.log v2.log

Now, for some specific code:

@MappedSuperclass
@Getter
@Accessors(chain = true)
@EqualsAndHashCode
public class Id<T extends Id<?>> {
  @Column(unique = true, length = 16, nullable = false)
  @jakarta.persistence.Id
  @Convert(converter = UuidBase64Type.class)
  private UUID id = UUID.randomUUID();

  @SuppressWarnings("unchecked")
  public T setId(UUID id) {
    this.id = id;
    return (T) this;
  }
}


@Getter
@Setter
@Accessors(chain = true)
@Entity
public class Image extends Id<Image> {
  @Column(unique = true, length = 16, nullable = false)
  @Convert(converter = UuidBase64Type.class)
  private UUID thumbId = UUID.randomUUID();

  private int position;
  private int size;
  private int thumbnailSize;
}

@Slf4j
public class UuidBase64Type implements AttributeConverter<UUID, byte[]> {
  @Override
  public byte[] convertToDatabaseColumn(UUID attribute) {
    return toBytes(attribute);
  }

  @Override
  public UUID convertToEntityAttribute(byte[] dbData) {
    return toUuid(dbData);
  }

  private UUID toUuid(byte[] bytes) {
    if (bytes == null || bytes.length == 0) {
      return null;
    }
    long mostSignificantBits =
        Longs.fromBytes(
            bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7]);
    long leastSignificantBits =
        Longs.fromBytes(
            bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15]);
    return new UUID(mostSignificantBits, leastSignificantBits);
  }

  private byte[] toBytes(UUID uuid) {
    if (uuid == null)  {
      return new byte[]{};
    }
    ByteBuffer bb = ByteBuffer.wrap(new byte[16]);
    bb.putLong(uuid.getMostSignificantBits());
    bb.putLong(uuid.getLeastSignificantBits());
    return bb.array();
  }
}

@Repository
public interface ImageRepository extends JpaRepository<Image, UUID> {}

class TheIntegrationTestThatIsFailing {
  @Test
  void teacher_can_delete_gallery() {
    // GIVEN
    imageRepository.saveAndFlush(
        new Image()
            .setPosition(0)
            .setSize(10)
            .setThumbnailSize(1));
  }
}

The strange part is, not all of my entities are failing (but still, quite a lot of them). The above example can be made to work with a single change, namely the removal of the thumbId field from the Image class:

@Getter
@Setter
@Accessors(chain = true)
@Entity
public class Image extends Id<Image> {
  private int position;
  private int size;
  private int thumbnailSize;
}

This exact same code works fine if I switch back to spring-boot v3.0.0-M3.

Comment From: whydoievenneedthis

I have prepared a repo with minimal code which showcases the exception at startup time: https://github.com/whydoievenneedthis/spring-hibernate-error

Comment From: whydoievenneedthis

Nevermind, this is most probably on hibernate side. I've opened a ticket with them. https://hibernate.atlassian.net/browse/HHH-15417, in case you want to keep track.