stark.activerecord.services.DSL.scala Maven / Gradle / Ivy
The newest version!
package stark.activerecord.services
import javax.persistence.criteria._
import stark.activerecord.macroinstruction.ActiveRecordMacroDefinition
import stark.activerecord.services.DSL.{DSLExecuteQuery, DSLSelectionQuery, QueryContext}
import scala.collection.JavaConversions
import scala.language.dynamics
import scala.language.experimental.macros
import scala.reflect.runtime.universe._
import scala.reflect.{ClassTag, classTag}
/**
* ActiveRecord DSL
*
* @author Jun Tsai
* @since 2016-03-09
*/
object DSL {
//DSL Context using DynamicVariable method
private[activerecord] val dslContext = new scala.util.DynamicVariable[QueryContext](null)
//Execute Query type
type DSLExecuteQuery[T] = ConditionBuilder[T] with Execute[T] with Limit
//Selection Query
type DSLSelectionQuery[T,R] = ConditionBuilder[T] with Limit with Fetch[R] with OrderBy with GroupBy
//DSLQuery
type DSLQuery={ }
//Query Context
private[activerecord] case class QueryContext(builder:CriteriaBuilder,query:DSLQuery,root:Root[_]){
var isMultiSelection = false
}
/**
* select method
*
* @tparam T entity type
* @return Selection step
*/
def select[T:ClassTag]: SelectStep[T,T]={
lazy val clazz = classTag[T].runtimeClass.asInstanceOf[Class[T]]
lazy val queryBuilder = ActiveRecord.entityManager.getCriteriaBuilder
lazy val query = queryBuilder.createQuery(clazz)
lazy val root = query.from(clazz)
implicit lazy val queryContext = QueryContext(queryBuilder,query,root)
new SelectStep[T,T](clazz)
}
/**
* selection field
*
* @param fields selection field
* @tparam T entity type
* @return selection step
*/
def select[T:ClassTag](fields:SelectionField*): SelectStep[T,Array[Any]]={
lazy val clazz = classTag[T].runtimeClass.asInstanceOf[Class[T]]
lazy val queryBuilder = ActiveRecord.entityManager.getCriteriaBuilder
lazy val query = queryBuilder.createQuery(clazz)
lazy val root = query.from(clazz)
implicit lazy val queryContext = QueryContext(queryBuilder,query,root)
new SelectStep[T,Array[Any]](clazz).apply(fields:_*)
}
/**
* delete entity
*
* @tparam T entity type
* @return delete step
*/
def delete[T:ClassTag]: DeleteStep[T]={
lazy val clazz = classTag[T].runtimeClass.asInstanceOf[Class[T]]
lazy val queryBuilder = ActiveRecord.entityManager.getCriteriaBuilder
lazy val query = queryBuilder.createCriteriaDelete(clazz)
lazy val root = query.from(clazz)
implicit lazy val queryContext = QueryContext(queryBuilder,query,root)
new DeleteStep[T]()
}
/**
* update entity
*
* @tparam T entity type
* @return update step
*/
def update[T:ClassTag]:UpdateStep[T]={
lazy val clazz = classTag[T].runtimeClass.asInstanceOf[Class[T]]
lazy val queryBuilder = ActiveRecord.entityManager.getCriteriaBuilder
lazy val query = queryBuilder.createCriteriaUpdate(clazz)
lazy val root = query.from(clazz)
implicit lazy val queryContext = QueryContext(queryBuilder,query,root)
new UpdateStep[T]()
}
/**
* create field
*
* @param name field name
* @tparam T field type
* @return
*/
def column[T : TypeTag](name:String):Field[T]={
new JPAField[T](name)
}
}
class ConditionBuilder[R](implicit val context: QueryContext) extends ConditionsGetter {
private var condition:Option[Predicate] = None
def apply(fun: =>Condition):this.type={
and(fun)
this
}
def or(fun: =>Condition):this.type={
DSL.dslContext.withValue(context){
val currentCondition = fun
condition =Some(condition.fold(currentCondition){p=>context.builder.or(Array[Predicate](p,currentCondition):_*)})
}
this
}
def and(fun: =>Condition):this.type={
DSL.dslContext.withValue(context){
val currentCondition = fun
condition =Some(condition.fold(currentCondition){p=>context.builder.and(Array[Predicate](p,currentCondition):_*)})
}
this
}
override private[activerecord] def getConditions: Option[Predicate] = condition
}
class SelectStep[T,R](clazz:Class[T])(implicit val context: QueryContext) extends Fetch[R] with Limit with ConditionsGetter with OrderBy with GroupBy{
private lazy val criteriaQuery = context.query.asInstanceOf[CriteriaQuery[T]]
def where:DSLSelectionQuery[T,R]=new ConditionBuilder[T] with Limit with Fetch[R] with OrderBy with GroupBy
def apply(f:SelectionField*):this.type={
DSL.dslContext.withValue(context){
if(f.nonEmpty) {
val selection = context.builder.array(f.map(_.toSelection): _*)
criteriaQuery.select(selection.asInstanceOf[Selection[T]])
context.isMultiSelection = true
}
}
this
}
}
class UpdateStep[T](implicit val context: QueryContext) extends ExecuteStep[T] with Dynamic{
private lazy val criteriaUpdate = context.query.asInstanceOf[CriteriaUpdate[T]]
def applyDynamicNamed(name:String)(params:(String,Any)*):this.type=macro ActiveRecordMacroDefinition.updateMethodImpl[T,this.type]
def setWithType[F](field:String,value:F):this.type={
criteriaUpdate.set(field,value)
this
}
def setWithType[F](field:Field[F],value:F):this.type={
criteriaUpdate.set(field.fieldName,value)
this
}
def internalUpdate(params: (String, Any)*):this.type = {
params.foreach{
case (field,value) =>
criteriaUpdate.set(field,value)
}
this
}
}
class DeleteStep[T](implicit val context: QueryContext) extends ExecuteStep[T]{
}
abstract class ExecuteStep[T](implicit context:QueryContext) extends ConditionsGetter with Execute[T] with Limit{
def where:DSLExecuteQuery[T]=new ConditionBuilder[T] with Execute[T] with Limit
}
private[activerecord] trait GroupBy{
val context:QueryContext
def groupBy[T](field: Field[T]): this.type ={
context.query.asInstanceOf[CriteriaQuery[_]].groupBy(context.root.get(field.fieldName))
this
}
}
private[activerecord] trait OrderBy {
val context:QueryContext
def orderBy[T](field: Field[T]): this.type ={
orderBy(field.asc)
this
}
def orderBy[T](field: SortField[T]): this.type ={
if(field.isAsc)
context.query.asInstanceOf[CriteriaQuery[_]].orderBy(context.builder.asc(context.root.get(field.field.fieldName)))
else
context.query.asInstanceOf[CriteriaQuery[_]].orderBy(context.builder.desc(context.root.get(field.field.fieldName)))
this
}
}
private[activerecord] trait Limit{
private[services] var limitNum:Int = 0
private[services] var offsetNum:Int = 0
def limit(limit:Int):this.type={
this.limitNum = limit
this
}
def offset(offset:Int):this.type={
this.offsetNum = offset
this
}
}
private[activerecord]trait ConditionsGetter{
private[activerecord] def getConditions:Option[Predicate]=None
}
private[activerecord] trait Execute[A]{
this:Limit with ConditionsGetter =>
val context:QueryContext
def execute: Int ={
val entityManager = ActiveRecord.entityManager
val query = context.query match{
case q:CriteriaUpdate[A] =>
val criteriaUpdate = context.query.asInstanceOf[CriteriaUpdate[A]]
getConditions.foreach(criteriaUpdate.where)
entityManager.createQuery(criteriaUpdate)
case q:CriteriaDelete[A] =>
val criteriaDelete = context.query.asInstanceOf[CriteriaDelete[A]]
getConditions.foreach(criteriaDelete.where)
entityManager.createQuery(criteriaDelete)
}
if(limitNum >0 )
query.setMaxResults(limitNum)
if(offsetNum > 0)
query.setFirstResult(offsetNum)
val entityService = ActiveRecord.entityService
entityService.execute(query)
}
}
private[activerecord] trait Fetch[A] extends Iterable[A]{
this:Limit with ConditionsGetter =>
val context:QueryContext
private lazy val executeQuery:Stream[A]= fetchAsStream
private def fetchAsStream: Stream[A]={
val entityManager = ActiveRecord.entityManager
val criteriaQuery = context.query.asInstanceOf[CriteriaQuery[A]]
getConditions.foreach(criteriaQuery.where)
val query = entityManager.createQuery(criteriaQuery)
if(limitNum >0 )
query.setMaxResults(limitNum)
if(offsetNum > 0)
query.setFirstResult(offsetNum)
JavaConversions.asScalaBuffer(query.getResultList).toStream
}
override def iterator: Iterator[A] = executeQuery.toIterator
}