Search code examples
javaspring-boothibernateentityidentifier

Is There A Way to manage a Custom generated ID with IdentifierGenerator from Hibernate to serve more than one Entity with different prefix?


I have a custom ID generator that generates an UUID string with a prefix for my entities ID, but since I'm using different prefix for each entity I'm having to create one ID generation class for each Entity, is there a way to only use one class for this?

My ID generation class is this:

import java.io.Serializable;
import java.util.UUID;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;

public class ProductIdGenerator implements IdentifierGenerator{
    
    public static final String generatorName = "produtcIdGenerator";

    @Override
    public Serializable generate(SharedSessionContractImplementor arg0, Object arg1) throws 
HibernateException {
        
        String prefix = "PROD";
        String uuid = UUID.randomUUID().toString().substring(0, 8);

        return prefix + uuid;
    }
}

My Entities looks like this:

@Entity
public class Product {

    @Id
    @GeneratedValue(generator = ProductIdGenerator.generatorName)
    @GenericGenerator(name = ProductIdGenerator.generatorName, strategy = "net.ddns.mrq.util.ProductIdGenerator")
    @Column(name = "product_id")
    private String id;

    private String name;
    .
    .
    .

I have 8 entities and I had to create 8 classes like this for each of one them with different prefix.

  1. Is there a way to make this more dynamic and less "time consuming"?
  2. Is there a way to only change the prefix for each class without creating multiple id generation classes?

Solution

  • I can think of a couple of ways to solve this (which is basically the need for a custom IdentifierGenerator to be parameterized).

    One idea involves each entity implementing an interface that can return the appropriate ID prefix for that entity type. Since the target entity is passed to the generator's generate() method, the generator could cast it to that interface and ask it for the prefix to use.

    Another solution takes advantage of the fact that IdentifierGenerators can implement the org.hibernate.id.Configurable interface to have configuration "injected" into them, and the @GenericGenerator annotation supports setting those as @Parameters in the annotation. That way, each usage of @GenericGenerator can dictate what prefix it wants the custom generator to use. It would look something like this (note, this is untested code):

    public class ProductIdGenerator implements IdentifierGenerator, org.hibernate.id.Configurable {
    
        public static final String GENERATOR_NAME = "produtcIdGenerator";
        public static final String PREFIX_PARAM = "prefix";
    
        private String prefix = "";
    
        @Override
        public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
            this.prefix = params.getProperty(PREFIX_PARAM, "");
        }
    
        @Override
        public Serializable generate(SharedSessionContractImplementor session, Object entityObject) throws HibernateException {
            String uuid = UUID.randomUUID().toString().substring(0, 8);
    
            return prefix + uuid;
        }
    }
    

    References to it would look like this:

        @Id
        @GeneratedValue(generator = ProductIdGenerator.GENERATOR_NAME)
        @GenericGenerator(
                        name = ProductIdGenerator.GENERATOR_NAME,
                        strategy = "net.ddns.mrq.util.ProductIdGenerator",
                        parameters = {@Parameter(name = ProductIdGenerator.PREFIX_PARAM, value = "foo")})
        private String id;
    

    Personally, I find the second idea a little cleaner, but there's nothing wrong with the first that I can see. It's a matter of style.