-
Notifications
You must be signed in to change notification settings - Fork 31
Using Models
A pair of a case class and its companion object is mapped to a database table.
The case class, extends com.github.aselab.activerecord.ActiveRecord
, corresponds to records in the table and provides CRUD logic, etc.
The companion object, extends com.github.aselab.activerecord.ActiveRecordCompanion[T]
, corresponds to the table and provides query logic, etc.
Note :
T
is a type of corresponding ActiveRecord class
package models
import com.github.aselab.activerecord._
case class Person(var name: String, var age: Int) extends ActiveRecord
object Person extends ActiveRecordCompanion[Person]
By default, each field is mapped to a column in the database, where the convention for column names is all lower case separated by underscores. (e.g.createdDate
maps onto a column created_date
.)
It is possible to override a field’s column name with the com.github.aselab.activerecord.dsl.Column
annotation. For example to change the name you can do:
package models
import com.github.aselab.activerecord._
import dsl._
import java.util.Date
case class Group(
var name: String,
@Column("CreatedDate") createdDate: Date
) extends ActiveRecord
object Group extends ActiveRecordCompanion[Group]
if set attribute value for transient field, you can use the com.github.aselab.activerecord.dsl.Transient
annotation.
package models
import com.github.aselab.activerecord._
import dsl._
case class User(
var name: String,
) extends ActiveRecord {
@Transient var fieldName = "Not saving the field"
}
object User extends ActiveRecordCompanion[User]
Note : More information is available in the Squeryl manual.
The default (and strongly recommended) way of mapping nullable columns to fields is with the Option[] type.
If you use Scala ActiveRecord to create (or generate) your table schema, all fields have a not null constraint, and Option[] fields are nullable.
package models
import com.github.aselab.activerecord._
import 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() // => throws Exception 'NULL not allowed for column "FOO";'
Bar(None).save() // => OK
String
Boolean
Int
Long
Float
Double
BigDecimal
java.sql.Timestamp
java.util.Date
java.util.UUID
With mixin Timestamps
trait, automatically timestamps create and update operations.
(The timestamp table columns are created_at
and updated_at
. In model, 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
With mixin Datestamps
trait, automatically timestamps create and update operations.
(The datestamp table columns are created_on
and updated_on
. In model, 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
The Optimistic
trait support optimistic locking. Each update to the record increments the occVersionNumber
column and the locking facilities ensure that records instantiated twice will let the last one saved raise a StaleObjectException
if the first was also updated. Example:
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
Optimistic locking will also check for stale data when objects are destroyed. Example:
val b1 = Book.head
val b2 = Book.head
b1.name = "update"
b1.save
b2.delete // throws com.github.aselab.activerecord.StaleObjectException
A database schema is defined in the schema object that extends com.github.aselab.aselab.ActiveRecordTables
.
Note : Its fully qualified class name must be written in
application.conf
. (Database Settings)
Declare a val field with table[T]
in the schema object.
Note :
T
is a type of ActiveRecord classes
package models
import com.github.aselab.activerecord._
import com.github.aselab.activerecord.dsl._
object Tables extends ActiveRecordTables {
val people = table[Person]
// You can also specify its table name. Default is the same name as its class.
// val people = table[Person]("other_table_name")
}
Note : More information is available in the Squeryl manual.
Table column properties (unique, index, auto_increment etc.) are also defined table[T]
.
For example to change the column attributes you can do:
package models
import com.github.aselab.activerecord._
import com.github.aselab.activerecord.dsl._
object Tables extends ActiveRecordTables {
val people = table[Person]
on(people)(t => declare(
t.name is(unique, indexed("idxPeopleName"), dbType("varchar(255)")),
t.name defaultsTo("defaultName"),
t.age is(indexed)
))
}
You must call initialize
method of the schema object before using model classes.
And then you should call cleanup
method to release all resources.
If you are using Play with Guice to DI initialization logic with an AbstractModule
, it is possible that the default scala-activerecord
play plugin may have not fired yet and you will get a com.github.aselab.activerecord.SchemaSettingException
. To fix this, you should ensure that your module encapsulates calls to initialize
and cleanup
against your schema tables definition.
Tables.initialize
...
Tables.cleanup
Person("person1", 15).save()
Person.findBy("name", "person1") // => Some(Person("person1", 15))
Person.findBy("name", "not exists"). // => None
Person.findBy("name" -> "person1", "age" -> 15) // => Some(Person("person1", 15))
Person.where(_.age gte 10).toList // => List(Person("person1", 15))
Person.all.toList // => List(Person("person1", 15))
The fields should be declared as var.
val person = Person.findBy("name", "person1").get
person.name = "new name"
person.age = 21
person.save
If you want to make it immutable, you should declare all fields as constructor arguments and use auto generated copy method of the case class.
Note : You must also declare
override val id
becauseid
field is declared in base class.
case class Person(name: String, age: Int, override val id: Long = 0) extends ActiveRecord
val person = Person.findBy("name", "person1").get
person.copy(name = "new name", age = 21).save
// Delete single record
val person = Person.findBy("name", "person1").get
person.delete
// Delete all records
Person.deleteAll
The transaction
and inTransaction
functions are available.
import models._
import com.github.aselab.activerecord.dsl._
Person.inTransaction {
// transactional code block is here
}
transaction
causes a new transaction to begin and commit after the block’s execution, or rollback if an exception occurs. Invoking a transaction always cause a new one to be created, even if called in the context of an existing transaction.
inTransaction
will create a new transaction if none is in progress and commit it upon completion or rollback on exceptions. If a transaction already exists, it has no effect, the block will execute in the context of the existing transaction. The commit/rollback is handled in this case by the parent transaction block.