0

Adding this question for documentation purpose, and to check if there is any alternative solution.

I have an entity which has a composite key defined using @IdClass

data class TemplateId(var id: Long? = null, var version: Int = 0) : Serializable

@Entity
@IdClass(TemplateId::class)
data class Template(
    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    var id: Long? = null,

    @Id
    @Column(name = "version")
    var version: Int

    //Other columns
)

Idea is to have same ID for different versions of the Template. Using Sequence generator works as expected while inserting new template. But when I try to insert a new version row with same ID, @GeneratedValue overrides the given value and autoincrements to new value. Solution mentioned in JPA: Override Auto generated ID does not work.

sidgate
  • 14,650
  • 11
  • 68
  • 119
  • Just a small suggestion: why not use Envers? AFAIK such a scenario (entities having multiple revisions per id) is supported out of the box – crizzis Jul 26 '20 at 19:59

1 Answers1

0

I tried out following options and none worked.

  • As mentioned in the question, cannot use @GeneratedValue as it replaces the given value

  • Cannot replace @SequenceGenerator with custom generator (@GenericGenerator), doesn't work with composite key. It tries to cast Long value to IdClass TemplateId

  • I am using Postgres, so tried using column type SERIAL for id. This does not work with IDENTITY GenerationType on composite key. There is an existing issue : HHH-9662

  • Cannot use DB auto-increment with NULL value, postgres gives constraint violation error

  • "insertable"=false/ "updatable"=false on id column does not work for @Id columns

  • Similarly tried using hibernate's @DynamicInsert so that it will skip null column values in insert query, but even that doesn't work with @Id

Finally had to override the save function of Spring's JpaRepository to make it work

interface CustomTemplateRepository<S> {
    fun <E: S> save(entity: E): E
    fun <E: S> saveAndFlush(entity: E): E
}


class TemplateRepositoryImpl(
    private val jdbcTemplate: NamedParameterJdbcTemplate,
    @PersistenceContext private val entityManager: EntityManager
) : CustomTemplateRepository<Template> {
    override fun <E : Template> save(entity: E): E {
        if(entity.id == null)
            entity.id = jdbcTemplate.queryForObject("select nextval('sequence_generator')", 
                              mutableMapOf<String, Any>(), Long::class.java)
        entityManager.persist(entity)
        return entity
    }

    override fun <E : Template> saveAndFlush(entity: E): E {
        return save(entity).also {
            entityManager.flush()
        }
    }
}
sidgate
  • 14,650
  • 11
  • 68
  • 119