2

I keep googling and find that the only way is to use

@Id
@GeneratedValue(strategy = GenerationType.Identity)

But I already have a primary key, I just need another field that auto increments. It's really hard to code it by manual calculation.

erotsppa
  • 14,248
  • 33
  • 123
  • 181
  • Does this answer your question? [Mapping PostgreSQL serial type with Hibernate annotations](https://stackoverflow.com/questions/4979746/mapping-postgresql-serial-type-with-hibernate-annotations) – richyen Feb 12 '20 at 23:55
  • No, just tried @Column(name = "internalID") @Generated(GenerationTime.INSERT) private Long internalID; and it didn't do anything. Value is null after insert – erotsppa Feb 12 '20 at 23:59
  • What if you do `@Column(name = "internalID")` and then `@GeneratedValue(strategy=GenerationType.IDENTITY)`? – richyen Feb 13 '20 at 00:01
  • Nope, nulls after inserts – erotsppa Feb 13 '20 at 00:08
  • @erotsppa Could you explain why do you what it? What is the initial problem that you try to solve. – SternK Feb 13 '20 at 11:38
  • @SternK I have an entity that already has an id field. The id is a primary key and I cant change it in production. The id is not sequential for this table GenerationType = AUTO. Now I have a need to add a new column that shows a sequential number for a user for any new rows – erotsppa Feb 13 '20 at 19:07

2 Answers2

4

I see the following options:

1) You can use @Generated annotation.

You should have a table declared in the following way:

create sequence TST_DATA_SEQ increment by 1 start with 1;

create table TST_DATA (
   ...
   dat_auto integer default nextval('TST_DATA_SEQ'),
   ...
);

and appropriate column in the entity:

   @Generated(value = GenerationTime.INSERT)
   @Column(name = "dat_auto", insertable = false, updatable = false)
   private Long auto;

Note that according to the documentation:

Properties marked as generated must additionally be non-insertable and non-updateable.

So, hibernate will make additional query to populate the auto field after flushing.

   Data data = new Data();
   // filling fields except data.auto
   session.persist(data);
   session.flush();
insert into TST_DATA (dat_name, dat_id) 
values (?, ?)

Hibernate: /* get generated state com.example.hibernate.Data */
  select data_.dat_auto as dat_auto_0_ 
  from TST_DATA data_ 
  where data_.dat_id=?

2) You can use @GeneratorType annotation.

You should have an implementation of hibernate ValueGenerator. The simple example you can see below.

import java.math.BigInteger;
import org.hibernate.Session;
import org.hibernate.tuple.ValueGenerator;

public class MyGenerator implements ValueGenerator<Long> 
{
   public Long generateValue(Session session, Object owner)
   {
      return (
         (BigInteger) session
            .createNativeQuery("select nextval('TST_DATA_SEQ')")
            .getSingleResult()
         ).longValue();
   }
}

And then you can use it like this:

   @GeneratorType(type = MyGenerator.class, when = GenerationTime.INSERT)
   @Column(name = "dat_auto")
   private Long auto;

In this case you should not provide the default value for the column declaration as was required in n1. and the appropriate entity field should not have @Column(... insertable = false, updatable = false). Each time when this entity will be persisted, hibernate generates query:

select nextval('TST_DATA_SEQ')

and populate this value to the auto field.

SternK
  • 11,649
  • 22
  • 32
  • 46
  • Both of these solutions require create sequence TST_DATA_SEQ, do you know how to automate this as part of spring startup? I dont want to resort to my CD pipeline to do this kind of thing – erotsppa Feb 18 '20 at 19:13
  • 1
    Actually I tried your second solution, and it went into an infinite loop inside generateValue() – erotsppa Feb 18 '20 at 20:29
  • 1
    As for sequence creation, look at spring boot [documentation](https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/howto-database-initialization.html). I tested both proposed approachs on the simple application (with only hibernate 5.4.4.Final dependency) and they work fine. It looks like your problem with `an infinite loop inside generateValue()` is related to integration with your project. – SternK Feb 19 '20 at 09:42
  • @erotsppa, a bit too late, but adding `.setHibernateFlushMode(FlushMode.COMMIT)` prevents the infinite loop (at least in my case) – Hugo P Aug 30 '22 at 16:18
0

Perhaps you can try:

@GeneratedValue(GenerationType.strategy=SEQUENCE, generator="internal_seq")
@SequenceGenerator(name="internal_seq", sequenceName="internal_seq", allocationSize=1)
@Column(name="internalID")
public Long internalID;
richyen
  • 8,114
  • 4
  • 13
  • 28