javahibernatejpaenumscompound-key

JPA compound key with foreign key and enum


Hi all,

Im trying to build all the jpa entities out of a legacy database. I have a recurrent problem: some "main" entities have a collection of "translations", where the primary key is the key of the "main" + the "language" identifier. Languages are stored on the database and on an Enum to ease their treatment.

The JPA implementation im using is Hibernate 4.

Hereunder, the current implementation:

Main: (Message)

@Entity
@Table(name="MESSAGES")
@NamedQueries({
public class Message implements Serializable {

  @Id
  @SequenceGenerator(name="MESSAGES_MESSAGEID_GENERATOR", sequenceName="SEQ_MSG_messageID")
  @GeneratedValue(strategy=GenerationType.SEQUENCE, generator="MESSAGES_MESSAGEID_GENERATOR")
  @Column(name="messageId")
  private long id;

  @OneToMany(mappedBy="message", fetch=FetchType.EAGER)
  private Set<MessageDesc> descriptions;
}

Translation: (MessageDesc)

@Entity
@Table(name="MESSAGEDESCS")
public class MessageDesc implements Serializable, Translatable {

  @EmbeddedId
  private MessageDescPK id;

  @NotNull  
  @Enumerated(EnumType.ORDINAL)
  @Column(name = "LANGUAGEID")
  private LanguageEnum language;

  @NotNull  
  @ManyToOne
  @JoinColumn(name="MESSAGEID")
  private Message message;
}

Translation Compound Key: (MessageDescPK)

@Embeddable
public class MessageDescPK implements Serializable {

  @Column(name="messageid", nullable = false, insertable = false)
  private long message;

  @Column(name="languageid", nullable = false, insertable = false)
  private int language;
}

Language: (LanguageEnum)

public enum LanguageEnum {
  FRENCH(0, "FR"),
  DUTCH(1, "NL");

  private int id;
  private String desc;
}

The problem is, when trying to persist a Main (Message) entity with some Translations on the descriptions and those translations having the Language and the Message setted, I keep receiving the following exception:

org.springframework.orm.jpa.JpaSystemException: org.hibernate.id.IdentifierGenerationException: null id generated for:class something.persistence.entity.message.MessageDesc; nested exception is javax.persistence.PersistenceException: org.hibernate.id.IdentifierGenerationException: null id generated for:class something.persistence.entity.message.MessageDesc
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:321)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:163)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111)

I dont understand why the PK is null after setting both the language and the message (already persisted) on the messageDesc instance. Any help, comment, clue will be appreciated.

Thanks!


Solution

  • This is how we solved this issue:

    MessageDesc:

    @NotNull    
    @Enumerated(EnumType.ORDINAL)
    @Column(name = "LANGUAGEID", insertable=false, nullable = false)
    private LanguageEnum language;
    
    @NotNull    
    @ManyToOne
    @JoinColumn(name="MESSAGEID", insertable=false, nullable = false)
    private Message message;
    
    @PrePersist
    private void prePersist(){
        if (getId() == null){
            MessageDescPK id = new MessageDescPK();
            //the way this enum is persisted is EnumType.ORDINAL
            id.setLanguage(getLanguage().ordinal());
            id.setMessage(getMessage().getId());
            this.setId(id);
        }
    }
    

    MessageDescPk:

    @Embeddable
    public class MessageDescPK implements Serializable {
        //default serial version id, required for serializable classes.
        private static final long serialVersionUID = 1L;
    
        @Column(name="messageid", nullable = false, insertable = false, updatable=false)
        private long message;
    
        @Column(name="languageid", nullable = false, insertable = false, updatable=false)
        private int language;
    

    Hope this helps :)