andrey.ladniy
andrey.ladniy

Reputation: 1674

How to organise the entity model for play framework 2 when working with anorm

I have a table with a set of fields, and foreign keys.

When working with Ebean I can specify which fields to fill in a request, starting only with the id ending with external relations (not always need to have all the fields from the database in the class). It uses only one class tied to the table.

@Entity
public class Product extends Model{   

@Id
public Long id;

@Constraints.Required
public String name;

@Lob
public String description;

@ManyToOne
public Trademark trademark;

...

}

Q1. When working with Anorm encouraged to use case class, but I need to specify all the possible fields of the case class and fill them. And if I need an object only with fields Id and Name, or only ref object with Id? I have to describe another class?

case class ProductRef (id: Pk[Long] = NotAssigned)
case class Product (id: Pk[Long] = NotAssigned, name: String)
case class ProductWithTrademark (id: Pk[Long] = NotAssigned, name: String, trademark: Trademark)
...
case class Trademark (id: Pk[Long] = NotAssigned, name: String ="")

Q2. What to do if you can use this table as a foreign key in another table? Which case class to use?

case class Size(id: Pk[Long] = NotAssigned, name: String)
case class ProductSize(id: Pk[Long] = NotAssigned, product: ??? , size: Size)

Q3. Or it would be better always to fill all the fields with default values and use only one case class

case class Product(id: Pk[Long] = NotAssigned, name: String="", trademark: Trademark = Trademark())
case class Trademark(id: Pk[Long] = NotAssigned, name: String="")
case class Size(id: Pk[Long] = NotAssigned, name: String = "")
case class ProductSize(id: Pk[Long] = NotAssigned, product: Product = Product(), size: Size = Size())

Q4. or there is the right decision about which I simply have no idea?

Upvotes: 0

Views: 518

Answers (1)

user908853
user908853

Reputation:

The usual practice when you have a couple of fields that can be null in your tables is to set them as Option in your case class. For example, instead of having:

case class Product (id: Pk[Long] = NotAssigned, name: String)
case class ProductWithTrademark (id: Pk[Long] = NotAssigned, name: String, trademark: Trademark)

You would have:

case class Product (id: Pk[Long] = NotAssigned, name: String, trademark: Option[Trademark])

And then set your product parser to check if the Trademark is there or not.


The computer-database sample that comes with Play describes that exact same case. In that example they used the following case classes to describe their models:

case class Company(id: Pk[Long] = NotAssigned, name: String)
case class Computer(id: Pk[Long] = NotAssigned, name: String, introduced: Option[Date], discontinued: Option[Date], companyId: Option[Long])

You can see that the foreign key is set as Option, as the a Computer model can (not have) a Company linked to it. The Computer parser is then described as follows:

val simple = {
    get[Pk[Long]]("computer.id") ~
    get[String]("computer.name") ~
    get[Option[Date]]("computer.introduced") ~
    get[Option[Date]]("computer.discontinued") ~
    get[Option[Long]]("computer.company_id") map {
      case id~name~introduced~discontinued~companyId => Computer(id, name, introduced, discontinued, companyId)
    }
}

Notice how when parsing the foreign key, get[Option[Long]] is used. The company parser is defined in the same manner, and when wanting to get both Computer and Company (If the foreign key company_id is set), they used the following parser that returns a tuple (Computer,Option[Company]):

val withCompany = Computer.simple ~ (Company.simple ?) map {
    case computer~company => (computer,company)
}

Upvotes: 2

Related Questions