モデル定義方法

モデル定義とテーブルスキーマ定義を行うことでScala ActiveRecordのDB操作を行うことができます。
本ページではモデル定義を記載します。
実際にDB操作で使用するためには別途、テーブルスキーマ定義方法 が必要となりますので合わせて参照ください。

モデル定義方法

データベースのテーブルに対して対応するモデル定義を行うためには、ケースクラス(case class)と
コンパニオンオブジェクト(Companion Object)の2つを定義します。

ケースクラスは com.github.aselab.activerecord.ActiveRecord クラスを継承したものを定義します。
ActiveRecord クラスを継承することで、テーブル内のレコードに対応したCRUDロジックなどが提供されます。

コンパニオンオブジェクトは、ケースクラスのモデルの型 T をパラメータに取る com.github.aselab.activerecord.ActiveRecordCompanion[T] を継承したものを定義します。
ActiveRecordCompanion[T] を継承することでテーブルに対応したクエリロジックなどが提供されます。

例: Userモデル定義 src/main/scala/models/User.scala

package models

import com.github.aselab.activerecord._

case class User(var name: String, var age: Int) extends ActiveRecord

object User extends ActiveRecordCompanion[User]

備考 : 上記のモデル定義に対応するデータベース上の users テーブル定義例は以下となります。
モデル操作を行いたい場合はあらかじめDBスキーマ定義を行ってください。

create table users (
 id bigint not null primary key auto_increment,
 name varchar(128) not null,
 age int not null
);

モデル変数からDBカラムへのマッピング

デフォルトの状態では、ケースクラス(モデル)の各フィールド変数はデータベース内のカラムにマッピングされます。
カラム名のマッピングはすべて小文字になりアンダースコアで区切られる形式となります。
(例: ケースクラスの createdDate フィールド は created_date カラムにマッピングされます)

com.github.aselab.activerecord.dsl.Column アノテーションをフィールド変数定義で指定することで任意のカラム名にマッピングすることが可能です。

例: カラム名変更 src/main/scala/models/Group.scala

以下の場合、 createDate フィールドは groups テーブルの CreatedDate カラムと対応します。

package models

import com.github.aselab.activerecord._
import com.github.aselab.activerecord.dsl._
import java.util.Date

case class Group(
  var name: String,
  @Column("CreatedDate") createdDate: Date
) extends ActiveRecord

object Group extends ActiveRecordCompanion[Group]

@Transient アノテーション

DBのテーブル上で管理したくないフィールド変数を定義する場合は com.github.aselab.activerecord.dsl.Transient アノテーションを付与してください。

package models

import com.github.aselab.activerecord._
import com.github.aselab.activerecord.dsl._

case class User(
  var name: String,
) extends ActiveRecord {
  @Transient var fieldName = "この変数はDBに保存されません"
}

object User extends ActiveRecordCompanion[User]

Nullを許容するカラムの場合(Option 型)

備考 : ラップ元のライブラリ squeryl での使用方法と同じとなります。

Nullを許容するカラムを定義する場合、Option型で包んで型を定義してください。
Scala ActiveRecordで自動的に生成するテーブルスキーマ定義では、Option型でない場合はNot Null制約が付与されます。

package models

import com.github.aselab.activerecord._
import com.github.aselab.activerecord.dsl._

case class Foo(var foo: String) extends ActiveRecord

object Foo extends ActiveRecordCompanion[Foo]

case class Bar(var bar: Option[String]) extends ActiveRecord

object Bar extends ActiveRecordCompanion[Bar]

...

Foo(null).save()  // => 'NULL not allowed for column "FOO";' といったDBのNot Null制約が発生し実行時例外が発生する
Bar(None).save()  // => OK

モデルで使用可能な型

ケースクラス内でDBテーブルとマッピング可能な型は以下の通りです。

自動タイムスタンプ付与

Timestampsトレイト

モデル定義時に Timestamps トレイトをmixinすると、データ作成時と更新時に自動的にタイムスタンプ(日時)を作成/更新します。
(DBテーブル定義ではcreated_atupdated_at カラムを追加してください。 モデルでは createdAt, updatedAt 変数が追加されます)

case class Foo(var foo: String) extends ActiveRecord with Timestamps

object Foo extends ActiveRecordCompanion[Foo]

// val foo = Foo("foo").create()
// foo.createdAt // => created Timestamp
// foo.updatedAt // => updated Timestamp

Datestampsトレイト

モデル定義時に Datestamps トレイトをmixinすると、データ作成時と更新時に自動的にタイムスタンプ(日付)を作成/更新します。
(DBテーブル定義ではcreated_onupdated_on カラムを追加してください。 モデルでは createdOn, updatedOn 変数が追加されます)

case class Foo(var foo: String) extends ActiveRecord with Datestamps

object Foo extends ActiveRecordCompanion[Foo]

// val foo = Foo("foo").create()
// foo.createdOn // => created Date
// foo.updatedOn // => updated Date

楽観的ロック

モデル定義時に Optimistic トレイトをmixinすると楽観的ロックが自動的に行われるようになります。
モデルのインスタンスでsaveによる更新が行われると、occVersionNumber 変数による楽観的ロック制御が行われ、
テーブル上の occ_version_number カラムの値が +1 増加されるようになります。
楽観的ロック実行時に複数のインスタンスによる保存が行われた場合(occ_version_number の値が変更前に他者に変更されていると検知した場合)、
StaleObjectException 例外をスローします。

例: 楽観的ロックの適用実装例

case class Book(var name: String, var price: Int) extends ActiveRecord with Optimistic

object Book extends ActiveRecordCompanion[Book]

val b1 = Book.head
val b2 = Book.head
b1.name = "update"
b1.save
b2.name = "other update"
b2.save  // throws com.github.aselab.activerecord.StaleObjectException

この楽観的ロック機能はレコード削除時も検知します。

val b1 = Book.head
val b2 = Book.head
b1.name = "update"
b1.save
b2.delete  // throws com.github.aselab.activerecord.StaleObjectException