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)
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.