4

Implementing my domain model in scala using case classes I got

abstract class Entity {
    val _id: Option[BSONObjectID]
    val version: Option[BSONLong]
}

and several case classes defining the different entities like

case class Person (
   _id: Option[BSONObjectID],
   name: String, 
   version: Option[BSONLong]
) extends Entity

What I need is a way to set the _id and version later on from a generic method which operates on an Entity because I have to share this behavior over all Entities and want to avoid writing it down hundreds of times ;-). I would love to be able to

def createID(entity: Entity): Entity = {
  entity.copy(_id = ..., version = ...)
}

...but of course this does not compile since an Entity has no copy-method. It is generated for each single case class by the compiler...

What is the best way to achieve this in scala?

To prevent somebody asking: I have to use case classes since this is what the third-party-library is extracting for me from the requests I get and the case class instances are what is serialized back to BSON / MongoDB later on...

jens
  • 453
  • 2
  • 12
  • possible duplicate of [Create common trait for all case classes supporting copy(id=newId) method](http://stackoverflow.com/questions/15078102/create-common-trait-for-all-case-classes-supporting-copyid-newid-method) – Régis Jean-Gilles Mar 09 '13 at 20:14
  • Why not factor out the _id and version bit? You would have a case class `Entity[T](_id: ..., version: ..., data: T)`. `T` would be probably another case class with the appropriate data. – Marius Danila Mar 09 '13 at 22:37
  • Sorry, but as I mentioned the case class instances (including id and version) are created by a third-party-library and therefore this is not an option here. – jens Mar 09 '13 at 23:17

2 Answers2

3

Indeed one can find a way to implement something like this at

Create common trait for all case classes supporting copy(id=newId) method

but since it is quite complicated for my use case I would prefer just to create two new classes

class MongoId(var id : BSONObjectID = null) {
    def generate = {
        id = BSONObjectID.generate
    }
}

class MongoVersion(var version: Long = 0) {
    def update = {
        version = System.currentTimeMillis
    }
}

and implemented the shared behavior regarding these fields there. Of course you have to change the definition of your base class accordingly:

abstract class Entity {
    def _id: MongoId
    def version: MongoVersion
}

To make it clear: This works only if the behavior you want to share over several case classes does only affect (in my case changes) one attribute ...

Community
  • 1
  • 1
jens
  • 453
  • 2
  • 12
0

Would implementing a trait work?

trait MongoIdHandler {
  def createId(entity : Entity) : Option[BSONObjectID] = { ..}
  def setVersion(version :  String) : Unit = { ..}
}

case class Person (..) with MongoIdHandler ..

If any of the instances require specialized versions of the id generator they can override the 'default' impl provided by the trait.

WestCoastProjects
  • 58,982
  • 91
  • 316
  • 560
  • No, unfortunately it does not help since I have to change the id-value of the Person since the Person is the object that gets serialized to BSON/MongoDB later on. I was not able to find a way to change the value of the field from any method of not knowing the exact type of the Entity (like Person in this example)... – jens Mar 09 '13 at 19:23