Relation nodes can be joined to allow restrictions of associated relations.
|
def findAssociation[T, F <: Record[T, F]](node: RelationNode[T, F]): Option[Association[T, R, F]] =
this.relation.findAssociation(node.relation)
def JOIN[T, J <: Record[T, J]](node: RelationNode[T, J],
on: Expression,
joinType: JoinType): JoinNode[PK, R, T, J] =
new JoinNode(this, node, joinType).ON(on)
def JOIN[T, J <: Record[T, J]](node: RelationNode[T, J],
joinType: JoinType = LEFT): JoinNode[PK, R, T, J] =
findAssociation(node) match {
case Some(a: Association[T, R, J]) => // many-to-one join
new ManyToOneJoin[PK, R, T, J](this, node, a, joinType)
case _ => node.findAssociation(this) match {
case Some(a: Association[PK, J, R]) => // one-to-many join
new OneToManyJoin[PK, R, T, J](this, node, a, joinType)
case _ =>
new JoinNode(this, node, joinType).ON(EmptyPredicate)
}
}
def INNER_JOIN[T, J <: Record[T, J]](node: RelationNode[T, J]): JoinNode[PK, R, T, J] =
JOIN(node, INNER)
def LEFT_JOIN[T, J <: Record[T, J]](node: RelationNode[T, J]): JoinNode[PK, R, T, J] =
JOIN(node, LEFT)
def RIGHT_JOIN[T, J <: Record[T, J]](node: RelationNode[T, J]): JoinNode[PK, R, T, J] =
JOIN(node, RIGHT)
def FULL_JOIN[T, J <: Record[T, J]](node: RelationNode[T, J]): JoinNode[PK, R, T, J] =
JOIN(node, FULL)
|
Equality & Others
Two nodes are considered equal if they wrap the same relation and share same aliases.
The hashCode method delegates to node's relation.
The canEqual method indicates that two nodes wrap the same relation.
The clone methods creates a shallow copy of this node (the underlying relation remains unchanged).
Finally, both toSql and toString return dialect specific SQL expression which appends an alias to relation's qualified name.
|
override def equals(that: Any): Boolean = that match {
case that: RelationNode[_, _] =>
this.canEqual(that) && this.alias == that.alias
case _ => false
}
override def hashCode: Int = relation.hashCode
def canEqual(that: Any): Boolean = that match {
case that: RelationNode[_, _] =>
this.relation == that.relation
}
def toSql: String = dialect.alias(relation.qualifiedName, alias)
override def clone(): this.type = super.clone.asInstanceOf[this.type]
override def toString: String = toSql
}
|
Joins
Relations can be joined within one query to allow applying restrictions on associated relations. The JoinNode class represents a join between two relations. We stick to a general convention called left associativity: two joined nodes with equal left nodes are considered equal:
(ci JOIN co) == ci
(ci JOIN co JOIN ca) == ((ci JOIN co) JOIN ca)
This way you can compose arbitrary complex query plans. The join condition (the ON subclause) can be either inferred from associations or specified manually using the ON method.
|
class JoinNode[PKL, L <: Record[PKL, L], PKR, R <: Record[PKR, R]](
protected var _left: RelationNode[PKL, L],
protected var _right: RelationNode[PKR, R],
protected var _joinType: JoinType) extends ProxyNode[PKL, L](_left) {
def left = _left
def right = _right
def joinType = _joinType
protected var _on: Expression = EmptyPredicate
def on = _on
def ON(expr: Expression): this.type = {
_on = expr
return this
}
def sqlOn = dialect.ON(this.on)
override def projections = left.projections ++ right.projections
def replaceLeft(newLeft: RelationNode[PKL, L]): this.type = {
this._left = newLeft
return this
}
def replaceRight(newRight: RelationNode[PKR, R]): this.type = {
this._right = newRight
return this
}
override def toSql = dialect.join(this)
override def clone(): this.type = super.clone()
.replaceLeft(this.left.clone)
.replaceRight(this.right.clone)
override def toString = "(" + left + " -> " + right + ")"
}
class ManyToOneJoin[PKL, L <: Record[PKL, L], PKR, R <: Record[PKR, R]](
childNode: RelationNode[PKL, L],
parentNode: RelationNode[PKR, R],
val association: Association[PKR, L, R],
joinType: JoinType) extends JoinNode[PKL, L, PKR, R](childNode, parentNode, joinType) {
override def on =
if (_on == EmptyPredicate)
association.joinPredicate(childNode.alias, parentNode.alias)
else _on
}
class OneToManyJoin[PKL, L <: Record[PKL, L], PKR, R <: Record[PKR, R]](
parentNode: RelationNode[PKL, L],
childNode: RelationNode[PKR, R],
val association: Association[PKL, R, L],
joinType: JoinType) extends JoinNode[PKL, L, PKR, R](parentNode, childNode, joinType) {
override def on =
if (_on == EmptyPredicate)
association.joinPredicate(childNode.alias, parentNode.alias)
else _on
}
|