Liftことはじめ その6 mapper ManyToMany

前述の記事の覚書
LiftのORMでManyToManyを実装。
AlbumとTrack間で実装。(同一Trackが、複数のAlbumに収録される事実を踏まえ。regular Albumとbest AlbumのTrack共用。)
まずは、Albumクラス。
model/Album.scala

package code.model

import net.liftweb.mapper._
import net.liftweb.util._
import net.liftweb.common._

object Album extends Album with LongKeyedMetaMapper[Album] {
  override def dbTableName = "albums"
}

class Album extends LongKeyedMapper[Album] with IdPK with ManyToMany with OneToMany[Long, Album] {
  def this(albumtitle: String) = {
    this()
    this.albumtitle(albumtitle)
  }
 
  def getSingleton = Album

  object albumtitle extends MappedString(this, 100) {
    override def validations =
      valMaxLen(100, "message must be under 100 characters long ") _ ::
      valMinLen(1, "you have to input") _ ::
      super.validations
  }
  object band extends LongMappedMapper(this, Band)

  def getBand(): Band = {
    Band.findAll(By(Band.id, band.get)).head
  } 

  object tracks extends MappedManyToMany(AlbumTracks, AlbumTracks.album, AlbumTracks.track, Track, OrderBy(AlbumTracks.seq, Ascending))
  object albumTracks extends MappedOneToMany(AlbumTracks, AlbumTracks.album, OrderBy(AlbumTracks.seq, Ascending))

}

(Albumクラスが、OneToManyトレイトもmixinしているのは、AlbumTracksクラスとOneToManyのアソシエーションを構成しているため)

続いて、Trackクラス

package code.model

import net.liftweb.mapper._
import net.liftweb.util._
import net.liftweb.common._

object Track extends Track with LongKeyedMetaMapper[Track] {
  override def dbTableName = "tracks"
}

class Track extends LongKeyedMapper[Track] with IdPK with ManyToMany with OneToMany[Long, Track] {
  def this(seq: Long, tracktitle: String) = {
    this()
    this.tracktitle(tracktitle)
  }

  def getSingleton = Track

  object tracktitle extends MappedString(this, 100) {
    override def validations =
      valMaxLen(100, "name length must be under 100 characters long ") _  ::
      valMinLen(1, "you have to input!!") _ ::
      super.validations
  }

  object albums extends MappedManyToMany(AlbumTracks, AlbumTracks.track, AlbumTracks.album, Album)

  object attaches extends MappedOneToMany(Attach, Attach.track, OrderBy(Attach.id, Ascending))
  object albumTracks extends MappedOneToMany(AlbumTracks, AlbumTracks.track, OrderBy(AlbumTracks.album, Ascending))

}

(Trackクラスが、OneToManyトレイトもmixinしているのは、AtachクラスとOneToManyのアソシエーションを構成しているため)

最後に、AlbumTracksクラス
model/AlbumTracks.scala

package code.model

import net.liftweb.mapper._
import net.liftweb.util._
import net.liftweb.common._

object AlbumTracks extends AlbumTracks with LongKeyedMetaMapper[AlbumTracks]

class AlbumTracks extends LongKeyedMapper[AlbumTracks] with IdPK {
  def this(album: Long, track: Long, seq: Long) = {
    this()
    this.seq(seq)
    this.album(album)
    this.track(track)
  }
  def getSingleton = AlbumTracks
  object album extends LongMappedMapper(this, Album)
  object track extends LongMappedMapper(this, Track)
  object seq extends MappedLong(this)
  def getTrack(): Track = Track.findAll(By(Track.id, track.get)).head
  def setSeq(seq: Long): Unit = {this.seq(seq)} 
}

ちょっと悩んだのは、IdPKトレイトをmixinしたこと、ManyToManyを構成する分には、
不要であるが、Aubum、Track間のアソシエーションを削除するためにmixinした。

以下は、利用のためのコード
(登録、更新)

          album.tracks += track
          album.save

と実装すればよいのだが、seq(曲順)の属性をAlbumTracksの属性にしたかったので、個別にインスタンス化した。

              val albumTrack: AlbumTracks = AlbumTracks.create.album(albumid.toLong).track(track.id.get).seq(seq.toLong)
              albumTrack.save

(削除)

           val album = Album.findAll(By(Album.id, getAlbumId().toLong)).head
           album.tracks -= track
           album.save

ソースコードこちら