Single Table Inheritance

20 views
Skip to first unread message

Mads Hartmann

unread,
Feb 4, 2010, 8:22:35 AM2/4/10
to Lift
Hello Everyone,

I'm currently trying to figure out how to map objects with inheritance
using the Mapper framework. I've got a Lab and a Scientist who inherit
from Source.

As I understand there's no direct way to do inheritance using the
Mapper framework so I'm trying to figure out how to implement 'Single
Table Inheritance'.

Right now I'm trying to create a single trait (Source) for the values
that they share called BaseSource and my idea is to mix it in both Lab
and Scientist. I would then define the table name of both scientist
and lab to be the same - I think this would work but what I'm not sure
of is the following:

- If both Scientist and Lab mixes in the IdPK trait and use the same
table, will the id still be unique?
- A Discovery has a source, how do i create MappedLongForeignKey to
the table they share (lab, scientist)?

I hope you'll be able to help me out :)

Thanks,
Mads Hartmann Jensen

Jeppe Nejsum Madsen

unread,
Feb 4, 2010, 12:17:07 PM2/4/10
to lif...@googlegroups.com
Mads Hartmann <mad...@gmail.com> writes:

I can't say I follow the above :-) Do you want to share tables, code or
both? Maybe list the table structure and explain how you would like to
map it....

/Jeppe

Mads Hartmann Jensen

unread,
Feb 4, 2010, 12:58:09 PM2/4/10
to lif...@googlegroups.com, lif...@googlegroups.com
hello Jeppe,

In my project I've got the following three models: A discovery, a
Scientist and a lab. The Discovery has been invented by someone, this
is either a single scientist or sometimes a lab - This is easily done
through inheritance (would create a superclass named source) but I'm
not sure how to do this so it maps nicely to the database.

Scientist and Lab only share one attribute so what I'm most interested
in is to be able express that a Discovery has s source that can be
either a lab or scientist :)

Hope this explains my problem more clearly, thanks for the help

Mads Hartmann

Sent from my iPhone

> --
> You received this message because you are subscribed to the Google
> Groups "Lift" group.
> To post to this group, send email to lif...@googlegroups.com.
> To unsubscribe from this group, send email to liftweb+u...@googlegroups.com
> .
> For more options, visit this group at http://groups.google.com/group/liftweb?hl=en
> .
>

Naftoli Gugenheim

unread,
Feb 4, 2010, 1:19:29 PM2/4/10
to lif...@googlegroups.com, lif...@googlegroups.com

-------------------------------------

David Pollak

unread,
Feb 4, 2010, 3:56:04 PM2/4/10
to lif...@googlegroups.com
Please take a look at the MegaProtoUser and MegaMetaProtoUser code for examples of how to create traits that can be mixed into classes.

Does that help?
--
Lift, the simply functional web framework http://liftweb.net
Beginning Scala http://www.apress.com/book/view/1430219890
Follow me: http://twitter.com/dpp
Surf the harmonics

Mads Hartmann

unread,
Feb 4, 2010, 6:40:46 PM2/4/10
to Lift
Yeah that helped, my lab and scientist now looks like this:

/------------------------------- code

trait BaseSourceTrait[ T <:BaseSourceTrait[T] ] extends
LongKeyedMapper[T] {

self: T =>

override def primaryKeyField = id
object id extends MappedLongIndex(this)

object name extends MappedPoliteString(this, 256)
object sourceType extends MappedEnum(this,SourceTypes)

object SourceTypes extends Enumeration {
val Scientist = Value("Scientist")
val Lab = Value("Lab")
}
}

class Scientist extends BaseSourceTrait[Scientist] {

def getSingleton = Scientist

object birth extends MappedInt(this)
object death extends MappedInt(this)
object nationality extends MappedPoliteString(this, 128)

}
object Scientist extends Scientist with LongKeyedMetaMapper[Scientist]
{
override def dbTableName = "Source"
}


class Lab extends BaseSourceTrait[Lab] {

def getSingleton = Lab

object institution extends MappedLongForeignKey(this, Institution)

}
object Lab extends Lab with LongKeyedMetaMapper[Lab] {
override def dbTableName = "Source"
}

/------------------------------- code ends

My only problem now is that I can't figure out how to implement the
following:

A Discovery (a mapped class) needs to have a foreign key to the source
that made the discovery, this could be either a lab or a scientist, so
normally i would have a field with a reference to the super-class of
both Scientist and Lab - However, I don't have a super-class, I just
have a trait :)

I would like to be able to write something like this:

/------------------------------- code starts again

class Discovery extends LongKeyedMapper[Discovery] with IdPK {

def getSingleton = Discovery

// primatives
object description extends MappedDateTime(this)
object year extends MappedInt(this)
object reference extends MappedPoliteString(this, 128)

// relationships
object source extends MappedLongForeignKey(this, BaseSourceTrait) //
<-- this is what i want.

}
object Discovery extends Discovery with LongKeyedMetaMapper[Discovery]

/------------------------------- code ends

As always I truely appreciate the help you guys are giving me :)

Thanks,
Mads Hartmann Jensen

On Feb 4, 9:56 pm, David Pollak <feeder.of.the.be...@gmail.com> wrote:
> Please take a look at the MegaProtoUser and MegaMetaProtoUser code for
> examples of how to create traits that can be mixed into classes.
>
> Does that help?
>

> On Thu, Feb 4, 2010 at 9:58 AM, Mads Hartmann Jensen <mads...@gmail.com>wrote:
>
>
>
>
>
> > hello Jeppe,
>
> > In my project I've got the following three models: A discovery, a Scientist
> > and a lab. The Discovery has been invented by someone, this is either a
> > single scientist or sometimes a lab - This is easily done through
> > inheritance (would create a superclass named source) but I'm not sure how to
> > do this so it maps nicely to the database.
>
> > Scientist and Lab only share one attribute so what I'm most interested in
> > is to be able express that a Discovery has s source that can be either a lab
> > or scientist :)
>
> > Hope this explains my problem more clearly, thanks for the help
>
> > Mads Hartmann
>
> > Sent from my iPhone
>
> > On 04/02/2010, at 18.17, Jeppe Nejsum Madsen <je...@ingolfs.dk> wrote:
>

> >> liftweb+u...@googlegroups.com<liftweb%2Bunsu...@googlegroups.com >


> >> .
> >> For more options, visit this group at
> >>http://groups.google.com/group/liftweb?hl=en.
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "Lift" group.
> > To post to this group, send email to lif...@googlegroups.com.
> > To unsubscribe from this group, send email to

> > liftweb+u...@googlegroups.com<liftweb%2Bunsu...@googlegroups.com >


> > .
> > For more options, visit this group at
> >http://groups.google.com/group/liftweb?hl=en.
>
> --
> Lift, the simply functional web frameworkhttp://liftweb.net

> Beginning Scalahttp://www.apress.com/book/view/1430219890

Naftoli Gugenheim

unread,
Feb 4, 2010, 7:52:49 PM2/4/10
to lif...@googlegroups.com
Does my approach not work?

-------------------------------------
Mads Hartmann<mad...@gmail.com> wrote:

/------------------------------- code

self: T =>

def getSingleton = Scientist

def getSingleton = Lab

/------------------------------- code ends

/------------------------------- code starts again

def getSingleton = Discovery

/------------------------------- code ends

Thanks,
Mads Hartmann Jensen

To unsubscribe from this group, send email to liftweb+u...@googlegroups.com.

Mads Hartmann

unread,
Feb 5, 2010, 1:49:41 AM2/5/10
to Lift
Hey Naftoli,
I think something might have broken your first message, It's a blank
message if you view it through the web-interface:
http://groups.google.com/group/liftweb/browse_thread/thread/e2317e5dbaad4a65

If you have a solution I would love to hear it :)

On Feb 5, 1:52 am, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> Does my approach not work?
>
> -------------------------------------
>

Ross Mellgren

unread,
Feb 5, 2010, 12:32:04 PM2/5/10
to lif...@googlegroups.com
This is actually fairly common that messages Naftoli sends are empty, or don't get threaded onto the original discussion, or formatting comes out funny. Naftoli, what email client are you using?

-Ross

Naftoli Gugenheim

unread,
Feb 5, 2010, 12:56:54 PM2/5/10
to lif...@googlegroups.com
Peek -- www.getpeek.com
Please forward any bad messages to feed...@getpeek.com

-------------------------------------

Mads Hartmann

unread,
Feb 5, 2010, 1:06:41 PM2/5/10
to Lift
Naftoli would you please re-post your solution :) I could really use
the help

On Feb 5, 6:56 pm, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> Peek --www.getpeek.com
> Please forward any bad messages to feedb...@getpeek.com


>
> -------------------------------------
>
> Ross Mellgren<dri...@gmail.com> wrote:
>
> This is actually fairly common that messages Naftoli sends are empty, or don't get threaded onto the original discussion, or formatting comes out funny. Naftoli, what email client are you using?
>
> -Ross
>
> On Feb 5, 2010, at 1:49 AM, Mads Hartmann wrote:
>
>
>
>
>
> > Hey Naftoli,
> > I think something might have broken your first message, It's a blank
> > message if you view it through the web-interface:

> >http://groups.google.com/group/liftweb/browse_thread/thread/e2317e5db...

Naftoli Gugenheim

unread,
Feb 5, 2010, 1:34:52 PM2/5/10
to lif...@googlegroups.com
Basically, either use two protected LongMappedMappers and a public getter/setter that takes a boolean into account in both directions; or use a MappedLong, with
def obj = whicheverLookUpTable.find(this.is)
and
override def set(s: Source) = s match ...

Sorry for the brevity, if unclear please ask.

-------------------------------------
Mads Hartmann<mad...@gmail.com> wrote:

Mads Hartmann

unread,
Feb 5, 2010, 2:00:20 PM2/5/10
to Lift
Thank you for taking your time to answer my question

I don't think I fully understand how to implement any of the suggested
solutions, if you have the time I would love a code example :)

Thanks a lot,
Mads Hartmann Jensen

On Feb 5, 7:34 pm, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> Basically, either use two protected LongMappedMappers and a public getter/setter that takes a boolean into account in both directions; or use a MappedLong, with
> def obj = whicheverLookUpTable.find(this.is)
> and
> override def set(s: Source) = s match ...
>
> Sorry for the brevity, if unclear please ask.
>
> -------------------------------------
>

Naftoli Gugenheim

unread,
Feb 5, 2010, 2:15:52 PM2/5/10
to lif...@googlegroups.com
Not tested or compiled

object isLab extends MappedBoolean(this)
1.
def source: Box[Source] = if(isLab.is) lab.obj else scientist.obj
def source_=(s: Source) = s match {
case l: Lab => isLab(true); lab(l)
// same for scientist
}

2.
object source extends MappedLong {
def obj = if(isLab) Lab.find(is) else Scientist.find(is)
def obj_=(s: Source) = s match {
case _: Lab => set(s.id)
// same for scientist
}

The advantage of 1 is database FK constraints.
The advantage of 2 is fewer fields.

-------------------------------------
Mads Hartmann<mad...@gmail.com> wrote:

Mads Hartmann

unread,
Feb 5, 2010, 5:26:23 PM2/5/10
to Lift
Ah! It seems to be working now - Thanks a lot :)


On Feb 5, 8:15 pm, Naftoli Gugenheim <naftoli...@gmail.com> wrote:
> Not tested or compiled
>
> object isLab extends MappedBoolean(this)
> 1.
> def source: Box[Source] = if(isLab.is) lab.obj else scientist.obj
> def source_=(s: Source) = s match {
>   case l: Lab => isLab(true); lab(l)
>   // same for scientist
>
> }
>
> 2.
> object source extends MappedLong {
>   def obj = if(isLab) Lab.find(is) else Scientist.find(is)
>   def obj_=(s: Source) = s match {
>     case _: Lab => set(s.id)
>     // same for scientist
>
> }
>
> The advantage of 1 is database FK constraints.
> The advantage of 2 is fewer fields.
>
> -------------------------------------
>

> ...
>
> read more »

Reply all
Reply to author
Forward
0 new messages