Search code examples
javamysqlhibernatejpaauto-increment

JPA or Hibernate to generate a (non primary key) column value, not starting from 1


I want a JPA/Hibernate (preferably JPA) annotation that can generate the value of a column, that is not a primary key and it doesn't start from 1.

From what I have seen JPA cannot do that with @GeneratedValue and @SequenceGenerator and @TableGenerator. Or with anything else.

I have seen a solution with an extra table, which I find is not elegant.

I can live with a Hibernate annotation, because I already have hibernate annotations.

I want to use @Generated but I cannot make it work and people claim that it is possible.

@Generated(GenerationTime.INSERT)
private long invoiceNumber;//invoice number

Update: an extra requirement, if the transaction is rolled back, we can't have a gap in the numbering. Anyone?


Solution

  • Here's what worked for me - we coded all of it in the service. Here's the entity:

    @Entity
    @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
    public class Registrant extends AbstractEntity {
        //....
        private long invoiceNumber;//invoice number
    
        @Entity
        public static class InvoiceNumberGenerator {
            @Id
            @GeneratedValue
            private int id;
            private long counter;
    
            public int getId() {
                return id;
            }
    
            public void setId(int id) {
                this.id = id;
            }
    
            public long getCounter() {
                return counter;
            }
    
            public void setCounter(long counter) {
                this.counter = counter;
            }
        }
    }
    

    And then we have a service that does the magic (actually there's no magic, all is done manually):

    public synchronized Registrant save(Registrant registrant) {
        long counter = getInvoiceNumber();
        registrant.setInvoiceNumber(counter);
    
        return registrantRepository.save(registrant);
    }
    
    private long getInvoiceNumber() {
        //mist: get the invoice number from the other table
        long count = registrantInvoiceNumberGeneratorRepository.count();
        if(count > 1) {
            throw new RuntimeException(": InvoiceNumberGenerator table has more than one row. Fix that");
        }
    
        Registrant.InvoiceNumberGenerator generator;
        if(count == 0) {
            generator = new Registrant.InvoiceNumberGenerator();
            generator.setCounter(1000001);
            generator = registrantInvoiceNumberGeneratorRepository.save(generator);
        } else {
            generator = registrantInvoiceNumberGeneratorRepository.findFirstByOrderByIdAsc();
        }
    
    
        long counter = generator.getCounter();
        generator.setCounter(counter+1);
        registrantInvoiceNumberGeneratorRepository.save(generator);
        return counter;
    }
    

    Note the synchronized method - so that nobody can get the same number.

    I can't believe there's nothing automatic that can do that.