1

I have an abstract base class marked @MappedSuperclass that has @Id @GeneratedValue set on its id property.

I also have an @Entity derived from it that needs to set its own IDs rather than relying on generation.

I have a large number of derived entities relying on the id being set in the @MappedSuperclass, and I'd prefer not to set up the id in each one.

How can I avoid the @GeneratedValue for just this one derived entity? I expected setting the ID manually to override generation and that doesn't appear to be the case.

DDL of id column (HSQL):

id bigint generated by default as identity (start with 1)

According to http://hsqldb.org/doc/guide/ch02.html#N104B3 id generation should only occur when a null value is specified for the id column.

Here is some example code:

@MappedSuperclass
class Parent {
    @Id @GeneratedValue
    private Long id;
}

@Entity
class Child extends Parent
    @PrePersist
    public void prePersist() {
        LOG.info("PrePersist with id #{}", this.id);
    }
}

Child t = new Child();
t.setId(123);

LOG.debug("Creating: Child #{}", t.getId());
childRepository.save(t); // Spring Data JPA Repositories
LOG.debug("Created: Child #{}", t.getId());

// Outputs:
// Creating: Child #123
// PrePersist with id #123 
// Created: Child #1

Edit:

It turns out that the default @GeneratedValue strategy doesn't allow specification of ids. The database will always generate them. It turns out I needed to write a custom ID generator...

public class CustomIdGenerator extends SequenceStyleGenerator {

    @Override
    public Serializable generate(SessionImplementor session, Object object)
        throws HibernateException {
        Serializable id = session.getEntityPersister(null, object)
            .getClassMetadata().getIdentifier(object, session);
        return id != null ? id : super.generate(session, object);
    }

}

... and then annotate the parent's ID field as follows ...

@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "CustomIdGenerator")
@GenericGenerator(name = "CustomIdGenerator", strategy = "com.foo.CustomIdGenerator")
private Long id;

This causes a sequence table or native sequence to be used for ID generation, but defers to IDs set on the entity.

Gabriel Bauman
  • 2,270
  • 1
  • 22
  • 36

1 Answers1

1

Edit: Working answer: Ok, now I see that there is no way to do this in pure JPA-way, but if you're using Hibernate you might try to write custom SequenceGenerator as it is described here. Basically, it will generate a value (call to super.generate()) only when the id is not set.

Community
  • 1
  • 1
rapasoft
  • 931
  • 9
  • 16
  • I've added some examples and the DDL. You appear to understand the problem quite well. – Gabriel Bauman Apr 08 '16 at 20:00
  • Ok, now I see that there is no way to do this in pure JPA-way, but if you're using Hibernate you might try to write custom `SequenceGenerator` as [it is described here](http://stackoverflow.com/questions/3194721/bypass-generatedvalue-in-hibernate-merge-data-not-in-db). Basically, it will generate a value (call to `super.generate()`) only when the id is not set. – rapasoft Apr 08 '16 at 20:54
  • Thanks for the custom SequenceGenerator link. If you'll update your answer, I'll accept it. I'll update the question with the code I used. – Gabriel Bauman Apr 08 '16 at 23:15