|  | package ru.circumflex.orm
import ru.circumflex.core._
import java.lang.reflect.Method
 | 
      
        | RelationsLike records, relations are alpha and omega of relational theory and, therefore, of Circumflex ORM API. In relational model a relation is a data structure which consists of a heading and an unordered set of rows which share the same type. Classic relational databases often support two type of relations, tables and views. In Circumflex ORM the relation contains record metadata and various operational information. There should be only one relation instance per application, so by convention the relations should be the companion objects of corresponding records: // record definition
class Country extends Record[String, Country] {
  val code = "code".VARCHAR(2).NOT_NULL
  val name = "name".TEXT.NOT_NULL
  def PRIMARY_KEY = code
  def relation = Country
}
// corresponding relation definition
object Country extends Country with Table[String, Country] {
}
 The relation should also inherit the structure of corresponding record so that it could be used to compose predicates and other expressions in a DSL-style. | trait Relation[PK, R <: Record[PK, R]] extends Record[PK, R] with SchemaObject { this: R =>
  protected var _initialized = false
 | 
      
        | CommonsIf the relation follows default conventions of Circumflex ORM (about  companion objects), then record class is inferred automatically. Otherwise  you should override the recordClassmethod. |   val _recordClass: Class[R] = Class.forName(
    this.getClass.getName.replaceAll("\\$(?=\\Z)", ""),
    true,
    Thread.currentThread.getContextClassLoader
    ).asInstanceOf[Class[R]]
  def recordClass: Class[R] = _recordClass
 | 
      
        | By default the relation name is inferred from recordClassby replacing camelcase delimiters with underscores (for example, record with classShoppingCartItemwill have a relation with nameshopping_cart_item). You can overriderelationNameto use different name. |   val _relationName = camelCaseToUnderscore(recordClass.getSimpleName)
  def relationName = _relationName
  def qualifiedName = dialect.relationQualifiedName(this)
 | 
      
        | Default schema name is configured via the orm.defaultSchemaconfiguration property. You may provide different schema for different relations by overriding theirschemamethod. |   def schema: Schema = defaultSchema
 | 
      
        | The readOnly_?()method is used to indicate whether the DML operations are allowed with this relation. Tables usually allow them and views usually don't. |   def readOnly_?(): Boolean
 | 
      
        | The autorefresh_?()method is used to indicate whether the record should be immediately refreshed after every successfulINSERTorUPDATEoperation. By default it returnsfalseto maximize performance. However, if the relation contains columns with auto-generated values (e.g.DEFAULTclauses, auto-increments, triggers, etc.) then you should override this method. |   def autorefresh_?(): Boolean = false
 | 
      
        | Use the ASmethod to create a relation node from this relation with explicit alias. |   def AS(alias: String): RelationNode[PK, R] = new RelationNode(this).AS(alias)
  def findAssociation[T, F <: Record[T, F]](relation: Relation[T, F]): Option[Association[T, R, F]] =
    associations.find(_.parentRelation == relation)
        .asInstanceOf[Option[Association[T, R, F]]]
  protected val validation = new RecordValidator[PK, R]()
 | 
      
        | Simple queriesFollowing methods will help you perform common querying tasks: 
  getretrieves a record either from cache or from database by specifiedid;allretrieves all records. |   def get(id: PK): Option[R] =
    contextCache.cacheRecord(id, this,
      (this.AS("root")).map(r => r.criteria.add(r.PRIMARY_KEY EQ id).unique))
  def all: Seq[R] = this.AS("root").criteria.list
 | 
      
        | MetadataRelation metadata contains operational information about it's records by introspecting current instance upon initialization. |   protected var _methodsMap: Map[Field[_, R], Method] = Map()
  def methodsMap: Map[Field[_, R], Method] = {
    init()
    _methodsMap
  }
  protected var _fields: List[Field[_, R]] = Nil
  def fields: Seq[Field[_, R]] = {
    init()
    _fields
  }
  protected var _associations: List[Association[_, R, _]] = Nil
  def associations: Seq[Association[_, R, _]] = {
    init()
    _associations
  }
  protected var _constraints: List[Constraint] = Nil
  def constraints: Seq[Constraint] = {
    init()
    _constraints
  }
  protected var _indexes: List[Index] = Nil
  def indexes: Seq[Index] = {
    init()
    _indexes
  }
  private def findMembers(cl: Class[_]): Unit = {
    if (cl != classOf[Any]) findMembers(cl.getSuperclass)
    cl.getDeclaredFields
        .flatMap(f => try Some(cl.getMethod(f.getName)) catch { case _ => None })
        .foreach(processMember(_))
  }
  private def processMember(m: Method): Unit = {
    val cl = m.getReturnType
    if (classOf[ValueHolder[_, R]].isAssignableFrom(cl)) {
      val vh = m.invoke(this).asInstanceOf[ValueHolder[_, R]]
      processHolder(vh, m)
    } else if (classOf[Constraint].isAssignableFrom(cl)) {
      val c = m.invoke(this).asInstanceOf[Constraint]
      this._constraints ++= List(c)
    } else if (classOf[Index].isAssignableFrom(cl)) {
      val i = m.invoke(this).asInstanceOf[Index]
      this._indexes ++= List(i)
    }
  }
  private def processHolder(vh: ValueHolder[_, R], m: Method): Unit = vh match {
    case f: Field[_, R] =>
      this._fields ++= List(f)
      if (f.unique_?) this.UNIQUE(f)
      this._methodsMap += (f -> m)
    case a: Association[_, R, _] =>
      this._associations ++= List[Association[_, R, _]](a)
      this._constraints ++= List(associationFK(a))
      processHolder(a.field, m)
    case _ =>
  }
  private def associationFK(a: Association[_, R, _]) =
    CONSTRAINT(relationName + "_" + a.name + "_fkey")
        .FOREIGN_KEY(a.field)
        .REFERENCES(a.parentRelation, a.parentRelation.PRIMARY_KEY)
        .ON_DELETE(a.onDelete)
        .ON_UPDATE(a.onUpdate)
  protected[orm] def init(): Unit =
    if (!_initialized) this.synchronized {
      if (!_initialized) try {
        findMembers(this.getClass)
        dialect.initializeRelation(this)
        _fields.foreach(dialect.initializeField(_))
        this._initialized = true
      } catch {
        case e: NullPointerException =>
          throw new ORMException("Failed to initialize " + relationName + ": " +
              "possible cyclic dependency between relations. " +
              "Make sure that at least one side uses weak reference to another " +
              "(change `val` to `lazy val` for fields and to `def` for inverse associations).", e)
        case e: Exception =>
          throw new ORMException("Failed to initialize " + relationName + ".", e)
      }
    }
  protected[orm] def copyFields(src: R, dst: R): Unit = fields.foreach { f =>
    val m = methodsMap(f)
    val value = getField(src, f.asInstanceOf[Field[Any, R]]).value
    getField(dst, f.asInstanceOf[Field[Any, R]]).set(value)
  }
  protected[orm] def getField[T](record: R, field: Field[T, R]): Field[T, R] =
    methodsMap(field).invoke(record) match {
      case a: Association[T, R, _] => a.field
      case f: Field[T, R] => f
      case _ => throw new ORMException("Could not retrieve a field.")
    }
 | 
      
        | You can declare explicitly that certain associations should always be prefetched whenever a relation participates in a Criteriaquery. To do so simply call theprefetchmethod inside relation initialization code. Note that the order of association prefetching is important; for more information refer toCriteriadocumentation. |   protected var _prefetchSeq: Seq[Association[_, _, _]] = Nil
  def prefetchSeq = _prefetchSeq
  def prefetch[K, C <: Record[_, C], P <: Record[K, P]](
      association: Association[K, C, P]): this.type = {
    this._prefetchSeq ++= List(association)
    return this
  }
 | 
      
        | Constraints & Indexes DefinitionCircumflex ORM allows you to define constraints and indexes inside the relation body using DSL style. |   protected def CONSTRAINT(name: String) = new ConstraintHelper(name, this)
  protected def UNIQUE(columns: ValueHolder[_, R]*) =
    CONSTRAINT(relationName + "_" + columns.map(_.name).mkString("_") + "_key")
        .UNIQUE(columns: _*)
 | 
      
        | Auxiliary ObjectsAuxiliary database objects like triggers, sequences and stored procedures can be attached to relation using addPreAuxandaddPostAuxmethods: the former one indicates that the auxiliary object will be created before the creating of all the tables, the latter indicates that the auxiliary object creation will be delayed until all tables are created. |   protected[orm] var _preAux: List[SchemaObject] = Nil
  def preAux: Seq[SchemaObject] = _preAux
  def addPreAux(objects: SchemaObject*): this.type = {
    objects.foreach(o => if (!_preAux.contains(o)) _preAux ++= List(o))
    return this
  }
  protected[orm] var _postAux: List[SchemaObject] = Nil
  def postAux: Seq[SchemaObject] = _postAux
  def addPostAux(objects: SchemaObject*): this.type = {
    objects.foreach(o => if (!_postAux.contains(o)) _postAux ++= List(o))
    return this
  }
 | 
      
        | EventsRelation allows you to attach listeners to certain lifecycle events of its records. Following events are available: 
  beforeInsertafterInsertbeforeUpdateafterUpdatebeforeDeleteafterDelete |   protected var _beforeInsert: Seq[R => Unit] = Nil
  def beforeInsert = _beforeInsert
  def beforeInsert(callback: R => Unit): this.type = {
    this._beforeInsert ++= List(callback)
    return this
  }
  protected var _afterInsert: Seq[R => Unit] = Nil
  def afterInsert = _afterInsert
  def afterInsert(callback: R => Unit): this.type = {
    this._afterInsert ++= List(callback)
    return this
  }
  protected var _beforeUpdate: Seq[R => Unit] = Nil
  def beforeUpdate = _beforeUpdate
  def beforeUpdate(callback: R => Unit): this.type = {
    this._beforeUpdate ++= List(callback)
    return this
  }
  protected var _afterUpdate: Seq[R => Unit] = Nil
  def afterUpdate = _afterUpdate
  def afterUpdate(callback: R => Unit): this.type = {
    this._afterUpdate ++= List(callback)
    return this
  }
  protected var _beforeDelete: Seq[R => Unit] = Nil
  def beforeDelete = _beforeDelete
  def beforeDelete(callback: R => Unit): this.type = {
    this._beforeDelete ++= List(callback)
    return this
  }
  protected var _afterDelete: Seq[R => Unit] = Nil
  def afterDelete = _afterDelete
  def afterDelete(callback: R => Unit): this.type = {
    this._afterDelete ++= List(callback)
    return this
  }
 | 
      
        | Equality & OthersTwo relations are considered equal if they share the record class and the same name. The hashCodemethod delegates to record class. The canEqualmethod indicates that two relations share the same record class. Record-specific methods derived from Recordthrow an exception when invoked against relation. |   override def equals(that: Any) = that match {
    case that: Relation[_, _] =>
      this.recordClass == that.recordClass &&
          this.relationName == that.relationName
    case _ => false
  }
  override def hashCode = this.recordClass.hashCode
  override def canEqual(that: Any): Boolean = that match {
    case that: Relation[_, _] =>
      this.recordClass == that.recordClass
    case that: Record[_, _] =>
      this.recordClass == that.getClass
    case _ => false
  }
  override def refresh(): Nothing =
    throw new ORMException("This method cannot be invoked on relation instance.")
  override def validate(): Nothing =
    throw new ORMException("This method cannot be invoked on relation instance.")
  override def INSERT_!(fields: Field[_, R]*): Nothing =
    throw new ORMException("This method cannot be invoked on relation instance.")
  override def UPDATE_!(fields: Field[_, R]*): Nothing =
    throw new ORMException("This method cannot be invoked on relation instance.")
  override def DELETE_!(): Nothing =
    throw new ORMException("This method cannot be invoked on relation instance.")
}
 | 
      
        | TableThe Tableclass represents plain-old database table which will be created to store records. | trait Table[PK, R <: Record[PK, R]] extends Relation[PK, R] { this: R =>
  def readOnly_?(): Boolean = false
  def objectName: String = "TABLE " + qualifiedName
  def sqlCreate: String = {
    init()
    dialect.createTable(this)
  }
  def sqlDrop: String = {
    init()
    dialect.dropTable(this)
  }
}
 | 
      
        | ViewThe Viewclass represents a database view, whose definition is designated by thequerymethod. By default we assume that views are not updateable, so DML operations are not allowed on view records. If you implement updateable views on backend somehow (with triggers in Oracle or rules in PostgreSQL), override thereadOnly_?method accordingly. | trait View[PK, R <: Record[PK, R]] extends Relation[PK, R] { this: R =>
  def readOnly_?(): Boolean = true
  def objectName: String = "VIEW " + qualifiedName
  def sqlDrop: String = {
    init()
    dialect.dropView(this)
  }
  def sqlCreate: String = {
    init()
    dialect.createView(this)
  }
  def query: Select[_]
}
 |