Relations and Foreign Keys in Squeryl - scala

Im using Scala, Squeryl and MySql to build a web app.
I found it easy to persist simple data as strings or integers. But what about when i have relations between objects and i need to use foreign keys. In my app i have areas, and sub areas which have an attribute of type Area (Area where they belong) so my Area and Subarea are like these
class Area(
var idArea: String,
#BeanProperty
var name:String,
#BeanProperty
var letter: String,
#BeanProperty
var color: String
)
extends Idable {
def this() = this("","", "","")
}
class SubArea(var idSubArea: String,
#BeanProperty
var name: String,
#BeanProperty
var area:Area
) extends Idable {
def this() = this("","",null )
How do i define the schema, so my SubArea table has an Area id, foreign key to my Area Table??
For the time being my SubArea schema is like these
object SubAreaSchema extends Schema {
val subAreas=table[SubArea]
on(subAreas)(subArea => declare(
subArea.id is (autoIncremented),
subArea.name is (unique)
))
}

You can define the relation in your schema with:
val areaToSubAreas = oneToManyRelation(areas, subAreas).via((a,sA) => s.idArea === sa.areaId)
To make that work, you would want to modify your SubArea class load the foreign key's id directly, as below with the areaId:String.
class SubArea(var idSubArea: String,
#BeanProperty
var name: String,
#BeanProperty
var areaId: String)
and then in the method body, if you want to have access to the object, you can use:
def area = areaToSubAreas.right(this)
which will yield an ManyToOne[Area] which you query, or use headOption on to convert to an Option[Area].
Conversely, if you need to reference the subareas on Area, you can use:
def subAreas = areaToSubAreas.left(this)
which will yield an OneToMany[Area] which can be iterated over, or you can also call toList.

Related

Is this possible with less code repetition

We're using case classes to represent the JSON objects transferred between the client and server. It's been working great except for one sticking point we've been living with for quite a while now and I wonder if anyone has a clever way around it.
Let's say I have a user object that has id, first name, last name and email address. Once a user has been saved to the database, he has an id (Int) assigned to him, so for all communication between the client and server dealing with existing users, the id is a required field. In fact, there is only one case when the id field is not required and that's when the user is first being saved. The way we currently deal with this is with a case class that looks like this:
case class User(id: Option[Int], firstName: String, lastName: String, email:String)
In all cases except the initial save, that id is Some and for the initial save id is always None so we find ourselves using id.getOrElse(0) quite often. (Sometimes we'll do a .get but it feels dirty.)
What I would love to have is an object with an id: Int field for existing users and an object with no id field at all for new users, but without declaring all the other fields twice in two separate case classes. However, I'm not seeing a way to do that conveniently. I'm also not fond of using a 'magic' number for the id field of new users.
Does anyone have a better solution to this issue?
case class User[+IdOpt <: Option[Int]](idOpt: IdOpt, firstName: String, lastName: String, email:String)
object User {
// Type aliases for convenience and code readability
type New = User[None.type]
type Saved = User[Some[Int]]
type Value = User[Option[Int]] // New or Saved
implicit class SavedOps(val user: Saved) extends AnyVal {
def id: Int = user.idOpt.get
}
}
Tests:
scala> val billNew = User(None, "Bill", "Gate", "bill#microsoft.com")
billNew: User[None.type] = User(None,Bill,Gate,bill#microsoft.com)
scala> billNew.id
<console>:17: error: value id is not a member of User[None.type]
billNew.id
^
scala> val billSaved = billNew.copy(idOpt = Some(1))
billSaved: User[Some[Int]] = User(Some(1),Bill,Gate,bill#microsoft.com)
scala> billSaved.id
res1: Int = 1
This is what we ended up going with for now.
trait Resource[T <: Option[Int]] {
def idOpt: T
}
object Resource {
type IsSome = Some[Int]
implicit class SomeOps[R <: Resource[IsSome]](val resource: R) {
def id: Int = resource.idOpt.get
}
}
This allows us to use it like this:
case class User[T <: Option[Int]](idOpt:T, firstName:String, lastName:String, email:String) extends Resource[T]
case class Company[T <: Option[Int]](idOpt:T, companyName: String) extends Resource[T]
val u1 = User(None, "Bubba", "Blue", "bubba#shrimp.com")
val u2 = User(Some(1), "Forrest", "Gump", "forrest#shrimp.com")
u1.id // <-- won't compile
u2.id // <-- compiles
Having a magic number is not a terrible idea if you hide it from the user. in fact it is a common pattern, Slick uses it for example. You can just ignore the id value for the objects to be inserted.
So you can start by making the the constructor package private
case class User private[db](id: Int, firstName: String, lastName: String, email:String)
And then provide a companion object for users to create it without id
object User{
def apply(firstName: String, lastName: String, email: String): User = User(-1, firstName, lastName, email)
}
And now you can construct it as if id wasn't required
val user = User("first","last","email")

How can one use Amazon's DynamoDBMapper in Scala?

I'm having trouble using Amazon's DynamoDBMapper in Scala code. The main sticking point is getting the JVM to recognize #DynamoDBHashkey when it is used in a case class, like:
case class MyCoolCaseClass(#DynamoDBHashKey(attributeName = "my_id") myId: String) {}
Any pointers from someone who has integrated this client library into a Scala project? (I'm hoping to not simply fallback to the low-level API, though that may be a decent decision once exhausting my options with the Mapper).
I had to do this:
import annotation.meta.beanGetter
import beans.BeanProperty
import com.amazonaws.services.dynamodbv2.datamodeling._
#DynamoDBTable(tableName="DEMOTAB")
case class DemoItem( // it's a case class for the free stuff, but can be ordinary class
#(DynamoDBHashKey #beanGetter) // would not work without meta annotation
#BeanProperty var id:String, // must be var or mapper can't instantiate one
#BeanProperty var number:Integer
) {
def this() = this(null, null) // needed by DynamoDB Mapper to instantiate
}
The DynamoDB mapper uses reflection to find the getters and setters. The SDK assumes Java-style conventions, i.e. that your getters and setters start with "get" or "is", and setters start with "set". You can see the reflection code on github.
I've been able to get it working, but it feels just like writing Java :(
#DynamoDBTable(tableName = "awesome_table")
class TheBestClass {
private var id : Integer = _
#DynamoDBHashKey
def getId() = id
def setId(_id: Integer) = id = _id
}
This works for me, including the boolean
#DynamoDBTable(tableName = "User")
case class User(
#(DynamoDBHashKey #field)
#(DynamoDBAutoGeneratedKey #field)
#BeanProperty var id: String,
#(DynamoDBAttribute #field)
#BeanProperty var firstName: String,
#(DynamoDBAttribute #field)
#BeanProperty var lastName: String,
#(DynamoDBAttribute #field)
#BeanProperty var active: Boolean
)
{
def this() = this(null, null, null, false)
}

Scala: Copying case classes with trait

I'm fairly new to Scala and I have a question about the best way to copy a case class while preserving data that comes from traits. For example, let's say I have the following:
trait Auditing {
var createTime: Timestamp = new Timestamp(System.currentTimeMillis)
}
case class User(val userName: String, val email: String) extends Auditing
val user = User("Joe", "joe#blah.com")
Then I want to make a new copy with one parameter changed:
val user2 = user.copy(email = "joe#newemail.com")
Now, in the example above, the property createTime does not get copied over because it is not defined in the constructor of the User case class. So my question is: assuming that moving createTime into the constructor is not an option, what is the best way for getting a copy of the User object that includes the value from the trait?
I'm using Scala 2.9.1
Thanks in advance!
Joe
You can override the copy method with that behavior.
case class User(val userName: String, val email: String) extends Auditing
{
def copy(userName = this.userName, email = this.email) {
val copiedUser = User(userName, email)
copiedUser.createTime = createTime
copiedUser
}
}
While I see no other solution than Reuben's, I don't understand the requirement to leave the constructor args untouched. This would be the most natural solution:
case class User(userName: String, email: String,
override val createTime:Timestamp = new Timestamp(System.currentTimeMillis))
extends Auditing
If you don't want the user to be able to overwrite createTime, you can still use:
case class User private (userName: String, email: String,
override val createTime:Timestamp) extends Auditing {
def this(userName: String, email: String) =
this(userName, email, new Timestamp(System.currentTimeMillis))
}
The only drawback is that you need to write new User("Joe", "joe#blah.com"), as the primary constructor is now private.
You might be better of not using a case class. You can easily implement the
functionality you need yourself. The below code implements the copy method you wanted, a constructor without new, hides the original constructor, and creates an extractor so that you can use User in case statements.
class User private(val userName: String,
val email: String,
val timeStamp: Timestamp =
new Timestamp(System.currentTimeMillis)) {
def copy(uName: String = userName,
eMail: String = email) =
new User(uName, eMail, timeStamp)
}
object User {
def apply(userName: String, email: String) =
new User(userName, email)
def unapply(u: User) = Some((u.userName, u.email, u.timeStamp))
}

How to model schema.org in Scala?

Schema.org is markup vocabulary (for the web) and defines a number of types in terms of properties (no methods). I am currently trying to model parts of that schema in Scala as internal model classes to be used in conjunction with a document-oriented database (MongoDB) and a web framework.
As can be seen in the definition of LocalBusiness, schema.org uses multiple inheritance to also include properties from the "Place" type. So my question is: How would you model such a schema in Scala?
I have come up with two solutions so far. The first one use regular classes to model a single inheritance tree and uses traits to mixin those additional properties.
trait ThingA {
var name: String = ""
var url: String = ""
}
trait OrganizationA {
var email: String = ""
}
trait PlaceA {
var x: String = ""
var y: String = ""
}
trait LocalBusinessA {
var priceRange: String = ""
}
class OrganizationClassA extends ThingA with OrganizationA {}
class LocalBusinessClassA extends OrganizationClassA with PlaceA with LocalBusinessA {}
The second version tries to use case classes. However, since case class inheritance is deprecated, I cannot model the main hierarchy so easily.
trait ThingB {
val name: String
}
trait OrganizationB {
val email: String
}
trait PlaceB {
val x: String
val y: String
}
trait LocalBusinessB {
val priceRange: String
}
case class OrganizationClassB(val name: String, val email: String) extends ThingB with OrganizationB
case class LocalBusinessClassB(val name: String, val email: String, val x: String, val y: String, val priceRange: String) extends ThingB with OrganizationB with PlaceB with LocalBusinessB
Is there a better way to model this? I could use composition similar to
case class LocalBusinessClassC(val thing:ThingClass, val place: PlaceClass, ...)
but then of course, LocalBusiness cannot be used when a "Place" is expected, for example when I try to render something on Google Maps.
What works best for you depends greatly on how you want to map your objects to the underlying datastore.
Given the need for multiple inheritance, and approach that might be worth considering would be to just use traits. This gives you multiple inheritance with the least amount of code duplication or boilerplating.
trait Thing {
val name: String // required
val url: Option[String] = None // reasonable default
}
trait Organization extends Thing {
val email: Option[String] = None
}
trait Place extends Thing {
val x: String
val y: String
}
trait LocalBusiness extends Organization with Place {
val priceRange: String
}
Note that Organization extends Thing, as does Place, just as in schema.org.
To instantiate them, you create anonymous inner classes that specify the values of all attributes.
object UseIt extends App {
val home = new Place {
val name = "Home"
val x = "-86.586104"
val y = "34.730369"
}
val oz = new Place {
val name = "Oz"
val x = "151.206890"
val y = "-33.873651"
}
val paulis = new LocalBusiness {
val name = "Pauli's"
override val url = "http://www.paulisbarandgrill.com/"
val x = "-86.713660"
val y = "34.755092"
val priceRange = "$$$"
}
}
If any fields have a reasonable default value, you can specify the default value in the trait.
I left fields without value as empty strings, but it probably makes more sense to make optional fields of type Option[String], to better indicate that their value is not set. You liked using Option, so I'm using Option.
The downside of this approach is that the compiler generates an anonymous inner class every place you instantiate one of the traits. This could give you an explosion of .class files. More importantly, though, it means that different instances of the same trait will have different types.
Edit:
In regards to how you would use this to load objects from the database, that depends greatly on how you access your database. If you use an object mapper, you'll want to structure your model objects in the way that the mapper expects them to be structured. If this sort of trick works with your object mapper, I'll be surprised.
If you're writing your own data access layer, then you can simply use a DAO or repository pattern for data access, putting the logic to build the anonymous inner classes in there.
This is just one way to structure these objects. It's not even the best way, but it demonstrates the point.
trait Database {
// treats objects as simple key/value pairs
def findObject(id: String): Option[Map[String, String]]
}
class ThingRepo(db: Database) {
def findThing(id: String): Option[Thing] = {
// Note that in this way, malformed objects (i.e. missing name) simply
// return None. Logging or other responses for malformed objects is left
// as an exercise :-)
for {
fields <- db.findObject(id) // load object from database
name <- field.get("name") // extract required field
} yield {
new Thing {
val name = name
val url = field.get("url")
}
}
}
}
There's a bit more to it than that (how you identify objects, how you store them in the database, how you wire up repository, how you'll handle polymorphic queries, etc.). But this should be a good start.

Scala Constructors, Named Arguments, and Implicit Getters/Setters

Is it possible to use named arguments in a Scala constructor, and later on override getters and setters without breaking the constructor interface or making the code extremely ugly?
Take the following bit of scala code
class Person( var FirstName: String, var LastName: String )
Nice and clean. This would create a simple class called person, which we could use in the following way
val john = new Person( FirstName="John", LastName="Doe" )
john.FirstName = "Joe"
println( john.FirstName )
Later, we decide we want to add some validation to the FirstName setter. As such, we create a new private local variable and override the getter and setter methods
class Person( var _FirstName: String, var _LastName: String ) {
def FirstName = _FirstName
def FirstName_= (value:String) = _FirstName = value
}
Still somewhat clean, however in order to do this, we've had to change the constructor argument names, thus breaking the external interface.
The first solution to this problem I came up with was
class Person {
var _FirstName:String = null
var LastName:String = null
def FirstName = _FirstName
def FirstName_= (value:String) = _FirstName = value
def this( FirstName: String, LastName: String ){
this()
this._FirstName = FirstName
this.LastName = LastName
}
}
Which is somewhat ugly and inelegant, and removes most of the nice reasons I was using scala in the first place.
Is there a better way of doing this?
tl;dr How to override getters/setters for members defined in the default constructor without making the code ugly or changing the public interface?
Did you consider using an companion object?
class Person private (f: String, l: String ) {
var FirstName = f
var LastName = l
}
object Person {
def apply(FirstName:String, LastName:String) =
new Person(FirstName, LastName)
}
If you're not already using implicit conversions to create the arguments, you can do something like this:
def validateName(s: String) = {
if (s.length>0 && s(0).isUpper) s
else throw new IllegalArgumentException(s+" is not a name!")
}
object Example {
private[Example] class ValidatedName(val s: String) { }
class Person(var firstName: ValidatedName, var lastName: String) { }
implicit def string2valid(s: String) = new ValidatedName(validateName(s))
implicit def valid2string(v: ValidatedName) = v.s
}
scala> new Example.Person("Joe","Schmoe")
res17: Example.Person = Example$Person#51887dd5
scala> new Example.Person("ee","cummings")
java.lang.IllegalArgumentException: ee is not a name!
It's not binary compatible, but it is source compatible (again, if the names weren't already relying upon implicit conversions).
Another slightly longer possibility is to create a stealth ancestor:
class CheckedPerson(private var first: String, var lastName: String) {
def firstName = first
def firstName_=(s: String) { first = validateName(s) }
}
class Person(firstName: String, lastName: String) extends
CheckedPerson(validateName(firstName),lastName) { }
for which I'm not sure about binary compatibility, but will definitely give source compatibility.
No. There is currently no way to do that, it's currently not the focus of research.
It is one of my major pet peeves I have with the language: There is no sensible way to combine constructor arguments and self-defined getter/setter methods.
If you're not happy with the functionality class Person( var FirstName: String, var LastName: String ) provides, it basically means "back to Java's verboseness".