So here's my dilemma: I have a domain model with a bunch of case classes in scala such as User
and Organization
. Within my data access layer (dao, repository, etc) I am using astyanax(a java library from netflix) and it's entity persister to save the object to a cassandra column family.
Here is some sample code for my cassandra/astyanax-backed DAOs (yes i know I need to do something more scala-ish, but I'm still learning =))
After reading through this long-winded description, I'm basically looking to see why annotated vals in the param list won't work when java does getDeclaredAnnotations()
on the Field
I would hate to have to go back and refactor everything so I can use the persister which makes it really simple to save an entity (i.e. manager.put(entity)
). If I want to keep using case classes so I can work with more immutable style scala and Lens
from scalaz, I'll then have to update the DAO and do all the persisting manually which can really kill time.
So, if anyone knows something that I'm not seeing, please let me know! Thanks in advance for taking the time to read through this.
Scenario 1 - Case Class
Astyanax fails to pick up the annotation @Id on val
@Entity case class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None, @Column(name = "created_on") override val createdOn: Option[Date] = None, @Column(name = "modified_on") override val modifiedOn: Option[Date] = None, @Column(name = "name") name: Option[String] = None, @Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn)
Scenario 2 - Class with companion object or class without companion object
Astyanax fails to pick up the @Id annotation on val
@Entity class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None, @Column(name = "created_on") override val createdOn: Option[Date] = None, @Column(name = "modified_on") override val modifiedOn: Option[Date] = None, @Column(name = "name") name: Option[String] = None, @Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn) object Organization { def apply(id: Option[UUID] = None, createdOn: Option[Date] = None, modifiedOn: Option[Date] = None, name: Option[String] = None, isPaidAccount: Boolean = false) = new Organization(id, createdOn, modifiedOn, name, isPaidAccount) }
Scenario 3 - Case class or class with val defined inside block
This works fine because it picks up theId
as being annotated with @Id
, but I don't want do do this because IdBaseEntity
already defines and id val
and defeats the whole purpose of inheritance and being able to pass id
to the superclass
@Entity case class Organization(@Id @Column(name = "id") override val id: Option[UUID] = None, @Column(name = "created_on") override val createdOn: Option[Date] = None, @Column(name = "modified_on") override val modifiedOn: Option[Date] = None, @Column(name = "name") name: Option[String] = None, @Column(name = "is_paid_account") isPaidAccount: Boolean = false) extends IdBaseEntity[UUID](id, createdOn, modifiedOn) { @Id @Column(name = "id") val theId: Option[UUID] = id }
The Data access portion
Way down in manager you will see a call to build()
. Astyanax examines the class that was passed in to withEntityType()
, which in this case is classOf[Organization]
Every one of my scenarios fails except #3 when I have a val declared inside the class block instead of the parameter list for the case class or a regular class/regular class with companion object. Astyanax says that there is know member of that class that is annotated with @Id
so it throws an exception. Before I dig any further, I figured I would ask the community about the nuances of annotation a scala class and sending it to a java library that does reflection. The source is nothing special. In fact here are the relevant lines where things fail: https://github.com/Netflix/astyanax/blob/master/astyanax-entity-mapper/src/main/java/com/netflix/astyanax/entitystore/EntityMapper.java#L89-120
class CassandraOrganizationDAO extends BaseCassandraDAO[Organization, UUID](Astyanax.context) with OrganizationDAO { val ColumnFamilyOrganizations: ColumnFamily[UUID, String] = new ColumnFamily[UUID, String]( "organizations", TimeUUIDSerializer.get(), StringSerializer.get(), ByteBufferSerializer.get()) val ColumnFamilyOrganizationMembers: ColumnFamily[UUID, UUID] = new ColumnFamily[UUID, UUID]( "organization_members", TimeUUIDSerializer.get(), TimeUUIDSerializer.get(), DateSerializer.get()) val manager: EntityManager[Organization, UUID] = new DefaultEntityManager.Builder[Organization, UUID]() .withEntityType(classOf[Organization]) .withKeyspace(getKeyspace()) .withColumnFamily(ColumnFamilyOrganizations) .build() // the rest of the class is omitted }