icon-arrow icon-check icon-mail icon-phone icon-facebook icon-linkedin icon-youtube icon-twitter icon-cheveron icon-download icon-instagram play close icon-arrow-uturn icon-calendar icon-clock icon-search icon-chevron-process icon-skills icon-knowledge icon-kite icon-education icon-languages icon-tools icon-experience icon-coffee-cup
Werken bij Whitehorses
Blog 08/04/2022

SQL Server with Hibernate UUID2 generation strategy: Solving the issue

Whitehorses

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.

Whitehorses
Mike Heeren /
Integratiespecialist

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

Symptoms after the change

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. 

Possible solutions

There are a couple of possibilities to resolve this issue: 

  1. Keep on using the guid generation strategy instead of uuid2; The GUIDGenerator documentation even states that this was specifically designed for the SQL Server NEWID() function. 
  2. Update field type from String to either byte[] or UUID; As also stated in the Hibernate issue, these field types don’t experience the same 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; 
        } 
     
    } 
  3. Keep the field type String, but always convert it to uppercase in the getId() method, so it matches the SQL Server implementation. Of course, you have to keep it null-safe:
@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(); 
    } 
 
} 

 

Conclusion

All 3 suggestions above provide a solution (or work around) for the problem. However, all solutions have their own positive and negative sides: 

  1. In my opinion, keep using the guid generation strategy instead is not a solution. It has been deprecated for a reason! 
  2. Changing the field type from String to either byte[] or UUID is, from a Java perspective, probably the cleanest way to resolve the issue. However, if you don’t only use the entity class to describe the database entity, but also as a Data Transfer Object (DTO) class (to marshal the data to another format like JSON, for example), this can cause other issues. Because then the DTO model would change, which could obviously impact the exposed interfaces. 
  3. Converting the value to uppercase in the getId() method does the job. However, it isn’t the cleanest solution in the code, because it adds some complexity. But if you would also use your entity object as a DTO, unlike option 2, this does not result in a change of the DTO model. 

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 plaatsen

Reactie toevoegen

Jouw e-mailadres wordt niet openbaar gemaakt.

Geen HTML

  • Geen HTML toegestaan.
  • Regels en alinea's worden automatisch gesplitst.
  • Web- en e-mailadressen worden automatisch naar links omgezet.
Whitehorses
Mike Heeren /
Integratiespecialist

Wil je deel uitmaken van een groep gedreven en ambitieuze integratiespecialisten? Stuur ons jouw cv!