Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
providing support for typesafe where...
- Loading branch information
Showing
18 changed files
with
1,221 additions
and
61 deletions.
There are no files selected for viewing
47 changes: 47 additions & 0 deletions
47
out/production/hibernate-query-dsl/br/com/caelum/hibernatequerydsl/ActiveCollection.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package br.com.caelum.hibernatequerydsl | ||
|
||
import scala.collection.JavaConversions._ | ||
import net.sf.cglib.proxy.Enhancer | ||
import org.hibernate.criterion.{Criterion, Restrictions} | ||
|
||
trait Cond { | ||
def crit:Criterion | ||
} | ||
class EqCond(field:String, value:Any) extends Cond { | ||
def crit = Restrictions.eq(field, value) | ||
} | ||
|
||
class ActiveCollection[T](var elements:List[T], query:PimpedCriteria[T,T])(implicit entityType:Manifest[T]) { | ||
|
||
private type Myself = ActiveCollection[T] | ||
private def loaded = Option(elements).isDefined | ||
|
||
private implicit def listToAC(l:List[T]):Myself = new Myself(elements, null) | ||
private implicit def queryToAC(q:PimpedCriteria[T,T]):Myself = new Myself(null, q) | ||
|
||
def grabThem():List[T] = { | ||
if(!loaded) { | ||
elements = query.asList[T].toList | ||
} | ||
elements | ||
} | ||
|
||
def take(k: Int):Myself = { | ||
query.using(_.setMaxResults(k)) | ||
} | ||
|
||
def filter(f: (T) => Cond):Myself = { | ||
query.and(applyRule(f).crit) | ||
} | ||
|
||
def find(f: (T) => Cond): Option[T] = { | ||
query.and(applyRule(f).crit).using(_.setMaxResults(1)).headOption | ||
} | ||
|
||
def applyRule(f: (T) => Cond):Cond = { | ||
val handler = new ComparisonCallback | ||
val proxy = Enhancer.create(entityType.erasure, handler).asInstanceOf[T] | ||
f(proxy) | ||
} | ||
|
||
} |
26 changes: 26 additions & 0 deletions
26
out/production/hibernate-query-dsl/br/com/caelum/hibernatequerydsl/ComparisonCallback.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package br.com.caelum.hibernatequerydsl | ||
|
||
import net.sf.cglib.proxy.InvocationHandler | ||
class ComparisonCallback extends InvocationHandler { | ||
|
||
def invoke(proxy:AnyRef,method:java.lang.reflect.Method,args:Array[AnyRef]) = { | ||
if(method.getReturnType!=classOf[String]) { | ||
throw new RuntimeException("We are not supporting anything but strings right now, sorry") | ||
} | ||
// TODO to implement others, we will need to use the cutest ThreadLocal ever | ||
// we can also use it with a SINGLE proxy per class by doing a list.an[User].getName | ||
|
||
// TODO switch to case or something else | ||
// TODO duplicated code, extract | ||
var _invoked = method.getName | ||
if(_invoked.startsWith("get")) { | ||
_invoked = _invoked.substring(3, _invoked.length) | ||
} else if(_invoked.startsWith("is")) { | ||
_invoked = _invoked.substring(2, _invoked.length) | ||
} | ||
val rest = if (_invoked.length() > 0) _invoked.substring(1,_invoked.length()) else "" | ||
_invoked = Character.toLowerCase(_invoked.charAt(0)) + rest | ||
_invoked | ||
} | ||
|
||
} |
40 changes: 40 additions & 0 deletions
40
out/production/hibernate-query-dsl/br/com/caelum/hibernatequerydsl/Expression.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package br.com.caelum.hibernatequerydsl | ||
|
||
import net.sf.cglib.proxy._ | ||
import java.lang.reflect.{ Method, Modifier } | ||
object Expression { | ||
val callback = () => new MethodInterceptor { | ||
implicit def string2WithRubyPowers(str: String) = new { | ||
def withFirstCharLowered = { | ||
str.substring(0, 1).toLowerCase + str.substring(1, str.length) | ||
} | ||
} | ||
|
||
val properties = scala.collection.mutable.ListBuffer[String]() | ||
def intercept(proxiedObject: Any, method: Method, params: Array[Object], methodProxy: MethodProxy) = { | ||
val GetterExpression = """(get)?(\w*){1}""".r | ||
method.getName match { | ||
case GetterExpression(_, part2) => { | ||
if (part2 != "toString") { | ||
properties += part2.withFirstCharLowered | ||
} | ||
} | ||
} | ||
if ((method.getReturnType.getModifiers & Modifier.FINAL) == 0) { | ||
proxy(method.getReturnType, this) | ||
} else { | ||
println(properties.mkString(".")) | ||
null | ||
} | ||
} | ||
} | ||
private def proxy[T](klass: Class[_], methodInterceptor: MethodInterceptor = callback()): T = { | ||
val enhancer = new Enhancer | ||
enhancer.setSuperclass(klass) | ||
enhancer.setCallback(methodInterceptor) | ||
enhancer.create.asInstanceOf[T] | ||
} | ||
def exp[T](implicit manifest: Manifest[T]): T = { | ||
proxy(manifest.erasure) | ||
} | ||
} |
37 changes: 37 additions & 0 deletions
37
...on/hibernate-query-dsl/br/com/caelum/hibernatequerydsl/InvocationMemorizingCallback.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package br.com.caelum.hibernatequerydsl | ||
|
||
import net.sf.cglib.proxy.InvocationHandler | ||
import org.hibernate.criterion.Restrictions | ||
import java.lang.reflect.Method | ||
|
||
class InvocationMemorizingCallback extends InvocationHandler { | ||
private var _invoked: String = "" | ||
|
||
def invokedPath = _invoked | ||
|
||
def invoke(proxy: AnyRef, method: java.lang.reflect.Method, args: Array[AnyRef]) = { | ||
_invoked = method.getName | ||
// TODO switch to case or something else | ||
if (_invoked.startsWith("get")) { | ||
_invoked = _invoked.substring(3, _invoked.length) | ||
} else if (_invoked.startsWith("is")) { | ||
_invoked = _invoked.substring(2, _invoked.length) | ||
} | ||
val rest = if (_invoked.length() > 0) _invoked.substring(1, _invoked.length()) else "" | ||
_invoked = Character.toLowerCase(_invoked.charAt(0)) + rest | ||
null | ||
} | ||
} | ||
|
||
object Pimps { | ||
implicit def xxx(qq:Any) = new PimpedCriteriaCondition(target) | ||
} | ||
|
||
class PimpedCriteriaCondition(target:Any){ | ||
val proxy = target.asInstanceOf[InvocationMemorizingCallback] | ||
def \==(value:Any) = { | ||
println(proxy.invokedPath+"aaaa") | ||
Restrictions.eq(proxy.invokedPath,value) | ||
} | ||
} | ||
|
18 changes: 18 additions & 0 deletions
18
out/production/hibernate-query-dsl/br/com/caelum/hibernatequerydsl/OrderThis.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package br.com.caelum.hibernatequerydsl | ||
|
||
import org.hibernate.criterion.Order | ||
|
||
// TODO if the method to construct this guy received the asc or desc as a second | ||
// parameter thourhg a import, then there would be no need for this extra guy or the | ||
// extra implicit. remove it? | ||
class OrderThis[T,P](path:String, val pimped:PimpedCriteria[T,P]) { | ||
|
||
import pimped.criteriaToPimped | ||
def asc():PimpedCriteria[T,P] = { | ||
pimped.criteria.addOrder(Order.asc(path)) | ||
} | ||
def desc():PimpedCriteria[T,P] = { | ||
pimped.criteria.addOrder(Order.desc(path)) | ||
} | ||
|
||
} |
141 changes: 141 additions & 0 deletions
141
out/production/hibernate-query-dsl/br/com/caelum/hibernatequerydsl/PimpedCriteria.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package br.com.caelum.hibernatequerydsl | ||
|
||
import org.hibernate.criterion._ | ||
import org.hibernate.criterion.Projections._ | ||
import org.hibernate.impl.CriteriaImpl | ||
import org.hibernate.transform.Transformers | ||
import org.hibernate.{Session, Criteria} | ||
import net.sf.cglib.proxy.Enhancer | ||
import scala.collection.JavaConversions._ | ||
import javax.persistence.criteria.Path | ||
|
||
/** | ||
* A criteria that will query on objects of type T, projecting | ||
* on type P. This criteria is backed by a hibernate criteria. | ||
*/ | ||
class PimpedCriteria[T,P](prefix:String, val criteria: Criteria) { | ||
|
||
import PimpedSession._ | ||
type Myself = PimpedCriteria[T,P] | ||
implicit def criteriaToPimped(partial:Criteria) = new PimpedCriteria[T,P](prefix, partial) | ||
|
||
val projections = projectionList | ||
val criteriaImpl = criteria.asInstanceOf[CriteriaImpl] | ||
|
||
if (criteriaImpl.getProjection != null) { | ||
projections.add(criteriaImpl.getProjection) | ||
} | ||
|
||
def unique[Y]: Y = criteria.uniqueResult.asInstanceOf[Y] | ||
|
||
def asList[Y]: List[Y] = criteria.list.asInstanceOf[java.util.List[Y]].toList | ||
|
||
def using(f:(Criteria) => Criteria):Myself = f(criteria) | ||
|
||
def list:List[P] = asList[P] | ||
|
||
def orderBy(order: Order):Myself = criteria.addOrder(order) | ||
|
||
// TODO use only one class per entity | ||
def orderBy(f:(T) => Unit)(implicit entityType:Manifest[T]) = { | ||
val path = evaluate(f) | ||
new OrderThis[T,P](prefix + path, this) | ||
} | ||
|
||
def orderBy2[Proj](f:(Proj) => Unit)(implicit manifest:Manifest[Proj]) = { | ||
val path = evaluate(f) | ||
new OrderThis[T,Proj](path, new PimpedCriteria[T,Proj]("", criteria)) | ||
} | ||
|
||
def headOption:Option[P] = using(_.setMaxResults(1)).list.toList.asInstanceOf[List[P]].headOption | ||
|
||
def join(field: String):Myself = criteria.createAlias(field, field) | ||
|
||
def join[Joiner](f:(T) => Joiner)(implicit entityType:Manifest[T]) = { | ||
val field = evaluate(f) | ||
new PimpedCriteria[Joiner, P](prefix + field + ".", criteria.createAlias(field, field)) | ||
} | ||
|
||
private def evaluate[K,X](f:(K) => X)(implicit entityType:Manifest[K]):String = { | ||
val handler = new InvocationMemorizingCallback | ||
val proxy = Enhancer.create(entityType.erasure, handler).asInstanceOf[K] | ||
f(proxy) | ||
handler.invokedPath | ||
} | ||
|
||
def has(toManyField: String):Myself = { | ||
criteria.add(Restrictions.isNotEmpty(toManyField)) | ||
} | ||
|
||
def includes(toManyField: String):Myself = { | ||
join(toManyField).has(toManyField) | ||
} | ||
|
||
def where(condition: Criterion):Myself = { | ||
criteria.add(condition) | ||
} | ||
|
||
def where:Myself = { this } | ||
|
||
def where(f:(T) => Unit)(implicit entityType:Manifest[T]) { | ||
val field = evaluate(f) | ||
} | ||
|
||
|
||
def and(condition: Criterion):Myself = { | ||
criteria.add(condition) | ||
} | ||
|
||
def count = criteria.setProjection(rowCount).uniqueResult.asInstanceOf[Long].intValue | ||
|
||
def first[Y] = criteria.setFirstResult(0).setMaxResults(1).unique[Y] | ||
|
||
def last[Y](implicit manifest: Manifest[Y]) = { | ||
val dirtySession = criteria.asInstanceOf[CriteriaImpl].getSession.asInstanceOf[Session] | ||
val size = dirtySession.from[Y].count | ||
criteria.setFirstResult(size.intValue - 1).unique[Y] | ||
} | ||
|
||
def groupBy(fields: String*):Myself = { | ||
fields.foreach(field => { | ||
projections.add(Projections.groupProperty(field)) | ||
}) | ||
criteria.setProjection(projections) | ||
} | ||
|
||
def select(fields: String*):Myself = { | ||
fields.foreach(field => { | ||
projections.add(Projections.property(field)) | ||
}) | ||
criteria.setProjection(projections) | ||
} | ||
|
||
def selectWithAliases(fields: Projection*):Myself = { | ||
fields.foreach(projections.add(_)) | ||
criteria.setProjection(projections) | ||
} | ||
|
||
def avg(field: String):Myself = { | ||
projections.add(Projections.avg(field)) | ||
criteria.setProjection(projections) | ||
} | ||
|
||
def sum(field: String):Myself = { | ||
projections.add(Projections.sum(field)) | ||
criteria.setProjection(projections) | ||
} | ||
|
||
def count(field: String):Myself = { | ||
projections.add(Projections.count(field)) | ||
criteria.setProjection(projections) | ||
} | ||
|
||
def distinct(field:String):Myself = { | ||
projections.add(Projections.distinct(Projections.property(field))) | ||
criteria.setProjection(projections) | ||
} | ||
|
||
def transformToBean[Y](implicit manifest: Manifest[Y]) = { | ||
new Transformer[Y,P](criteria.setResultTransformer(Transformers.aliasToBean(manifest.erasure))) | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
out/production/hibernate-query-dsl/br/com/caelum/hibernatequerydsl/PimpedQuery.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package br.com.caelum.hibernatequerydsl | ||
|
||
import org.hibernate.Query | ||
import scala.collection.JavaConversions._ | ||
|
||
class PimpedQuery(query: Query) { | ||
def withParams(params: (String, Any)*) = { | ||
params.foreach((param) => { | ||
query.setParameter(param._1, param._2) | ||
}) | ||
query | ||
} | ||
|
||
def unique[T]: T = query.uniqueResult.asInstanceOf[T] | ||
|
||
def asList[T]: List[T] = query.list.asInstanceOf[java.util.List[T]].toList | ||
|
||
def headOption[T]:Option[T] = { | ||
query.setMaxResults(1).list.asInstanceOf[List[T]].headOption | ||
} | ||
} |
Oops, something went wrong.