Scala Case Class as JPA EmbeddedId

It is a good idea to use case classes as EmbeddedId class. Id classes can be used to specify an embeddable composite primary key class owned by the entity (more…).
Scala Case classes provide a default equals and an “automatic” generation of class properties.

So lets create a really simple example Entity Class OSEmbeddedId.

@Entity
class OSEmbeddedId  {

  @EmbeddedId
  var id: OSEId = new OSEId()

  @MapsId("orgStructRootOrg")
  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "org_struct_root_org_id", nullable = false, updatable = false)
  @BeanProperty
  protected var orgStructRootOrg: Organisation = _
}

OSEmbeddedId should have two part primary Id. One with the name “ix” (filled with the value of a Sequence) and one as a relation to a Entity called Organisation.

The Scala case class in principle provides the primary key attributes as follows:

case class OSEId(var orgStructRootOrg: BigInteger, var ix: BigInteger)

But we have to solve some problems first:

  • we have to provide public bean Setter and Getter methods
  • case class attributes are private, so we have to tell the JPA provider to use the bean Setter and Getter
  • we have to put the property annotation to the bean Getter methods
  • we will need a default constructor without parameter

Adding annotations (and annotation annotations…) to a Scala case class can be a bit confusing. So we first create a IdClassAnnotation object to increase readability:

object IdClassAnnotations {
  type _GeneratedValue = GeneratedValue @beanGetter
  type _Column = Column @beanGetter
}

This objects simply maps the JPA annotations GeneratedValue and Column to new ones. The Scala annotation @beanGetter instructs the compiler to place them only to the bean Getter method.

Putting all together:

import IdClassAnnotations._

@Embeddable
@Access(AccessType.PROPERTY)
@SequenceGenerator(name = "org_struct_index_seq", sequenceName = "org_struct_index_sequence")
case class OSEId(
   @BeanProperty
   var orgStructRootOrg: BigInteger,

   @_GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "org_struct_index_seq")
   @_Column(name = "org_struct_ix", nullable = false, length = 20)
   @BeanProperty
   var ix: BigInteger) extends Serializable {
   
   def this() = this (null, null)
}

SequenceGenerator and _GeneratedValue are needed to create the Id values from the Sequence org_struct_index_sequence. The Scala annotation BeanProperty instructs the compiler to create public bean Getter and Setter methods. Access forces the JPA provider to use the bean methods and not the properties (which are private). And finally we have to provide a default constructor.

That’s simple, isn’t it?

Connect with me on Google+

Comments are closed.