After switching the Hibernate generation strategy from GUID to UUID2, we started experiencing issues with SQL Server unique identifier columns. In this article we’ll dive into the problem, the cause and how it can be resolved.
On a project I’m currently working on, we’re using a SQL Server database, where some of the ID fields are of the UNIQUEIDENTIFIER type. For example:
CREATE TABLE test_entity (
id UNIQUEIDENTIFIER NOT NULL PRIMARY KEY DEFAULT NEWID()
);
For exposing these records in our application, we specified them as JPA @Entity classes. Here we also configured that the guid strategy should be used when generating new IDs.
@Entity
@Table(name = "test_entity")
public class TestEntity {
@Id
@GenericGenerator(name = "generator", strategy = "guid")
@GeneratedValue(generator = "generator")
private String id;
public String getId() {
return id;
}
}
Only, when starting the application, we noticed warnings in the log file, like the following:
org.hibernate.id.GUIDGenerator: HHH000065: DEPRECATED : use [org.hibernate.id.UUIDGenerator] instead with custom [org.hibernate.id.UUIDGenerationStrategy] implementation
As can be found in the DefaultIdentifierGeneratorFactory class constructor, the org.hibernate.id.UUIDGenerator is represented by the uuid2 generation strategy. So, we decided to change the @GenericGenerator annotation values:
@GenericGenerator(name = "generator", strategy = "uuid2")
After this change, we started noticing issues with the generated IDs. When we would save a new entity into the database, the (generated) ID did not match the ID of that same entity when fetching it from the database. We proved this by a simple unit test, similar to the one below:
@Test
void verifyIdReturnedByCreateEqualToIdInDatabase() {
// Given
TestEntity newEntity = new TestEntity();
// When
repository.save(newEntity);
// Then
TestEntity savedEntity = repository.findAll().get(0);
assertEquals(savedEntity.getId(), newEntity.getId());
}
When executing this test, this would fail with a reason like the following:
org.opentest4j.AssertionFailedError:
Expected :9567253B-8F81-4EC4-8274-8577861F1896
Actual :9567253b-8f81-4ec4-8274-8577861f1896
As you can see, the expected value (so the value that was stored in the database) is uppercase, but the actual value (the generated ID that was stored in the entity) is lowercase.
This was also addressed to Hibernate via issue HHH-12943: SQL Server UNIQUEIDENTIFIER type uses an uppercase UUID String representation. However, Hibernate will not implement a fix, because Hibernate is actually following the UUID specifications (stating UUIDs should be lowercase), where SQL Server deviates from this standard.
There are a couple of possibilities to resolve this issue:
@Entity
@Table(name = "test_entity")
public class TestEntity {
@Id
@GenericGenerator(name = "generator", strategy = "uuid2")
@GeneratedValue(generator = "generator")
private UUID id;
public UUID getId() {
return id;
}
}
@Entity
@Table(name = "test_entity ")
public class TestEntity {
@Id
@GenericGenerator(name = "generator", strategy = "uuid2")
@GeneratedValue(generator = "generator")
private String id;
public String getId() {
return id == null ? null : id.toUpperCase();
}
}
All 3 suggestions above provide a solution (or work around) for the problem. However, all solutions have their own positive and negative sides:
To wrap up, both solution 2 and 3 work perfectly fine and are even proposed as solutions/workarounds by Hibernate. My personal preference would be solution 2, because your code remains cleaner. However, if there is a good reason why the field type should not change (so for example when the entity class is also used as DTO class), I’d go for solution 3.
Geen reacties
Geef jouw mening
Reactie plaatsenReactie toevoegen