Skip to content

Commit

Permalink
supporting typesafe queries using filter. also allowing lazy sql quer…
Browse files Browse the repository at this point in the history
…ies.
  • Loading branch information
guilhermesilveira committed Jun 26, 2011
1 parent 4e064a2 commit 7e51e7a
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 15 deletions.
@@ -0,0 +1,62 @@
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
def filter(obj:Object):Boolean
}
class EqCond(field:String, value:Object) extends Cond {
def crit = Restrictions.eq(field, value)
def filter(obj:Object) = (true)
}
// TODO extract list interface so we dont need the cases,
// can simply return the list and thats it
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 = {
loaded match {
case (false) => query.using(_.setMaxResults(k))
case (true) => new Myself(elements.take(k), null)
}
}

def filter(f: (T) => Cond):Myself = {
loaded match {
case (false) => query.and(applyRule(f).crit)
case (true) => elements.filter(toB(f))
}
}

def find(f: (T) => Cond): Option[T] = {
loaded match {
case (false) => query.and(applyRule(f).crit).using(_.setMaxResults(1)).headOption
case (true) => elements.find(toB(f))
}
}
def toB(f:(T) => Cond):((T) => Boolean) = {
throw new RuntimeException("going through the list")
}

def applyRule(f: (T) => Cond):Cond = {
val handler = new ComparisonCallback
val proxy = Enhancer.create(entityType.erasure, handler).asInstanceOf[T]
f(proxy)
}

}
@@ -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
}

}
32 changes: 17 additions & 15 deletions src/main/scala/br/com/caelum/hibernatequerydsl/PimpedSession.scala
Expand Up @@ -10,15 +10,26 @@ object PimpedSession {

implicit def pimpedCriteria2Criteria[T,P](pimped: PimpedCriteria[T,P]) = pimped.criteria

implicit def string2PimpedStringCondition(field: String) = new PimpedStringCondition(field)

implicit def hibernateQuery2PimpedQuery(query: Query) = new PimpedQuery(query)

implicit def code2PimpedCode[T](code:Code[T]) = new PimpedCode(code)

implicit def code2String[T](code:Code[T]) = new PimpedCode(code).toString

implicit def orderThisToPimped[T,P](order:OrderThis[T,P]) = order.asc

implicit def collectionToActive[T](elements:List[T], criteria:PimpedCriteria[T,T])(implicit t:Manifest[T]) = new ActiveCollection[T](elements, criteria)

implicit def acToList[T](ac:ActiveCollection[T]) = ac.grabThem

}

object TypeUnsafe {
implicit def string2PimpedStringCondition(field: String) = new PimpedStringCondition(field)
}
object TypeSafe {
implicit def string2Conditioner(field: String) = new StringConditioner(field)

}

class PimpedCode[T](code: Code[T]) {
Expand Down Expand Up @@ -77,19 +88,6 @@ class PimpedCode[T](code: Code[T]) {
def alias(newName: String) = Projections.property(evaluate).as(newName)
}

class PimpedQuery(query: Query) {
def withParams(params: (String, Object)*) = {
params.foreach((param) => {
query.setParameter(param._1, param._2)
})
query
}

def unique[T]: T = query.uniqueResult.asInstanceOf[T]

def asList[T]: java.util.List[T] = query.list.asInstanceOf[java.util.List[T]]
}

class PimpedStringCondition(field: String) {
def equal(value: Object) = Restrictions.eq(field, value)

Expand Down Expand Up @@ -117,6 +115,10 @@ class PimpedStringCondition(field: String) {

}

class StringConditioner(field: String) {
def equal(value: Object) = new EqCond(field, value)
}

class PimpedSession(session: Session) {

def all[T](implicit manifest:Manifest[T]) = {
Expand Down
@@ -0,0 +1,80 @@
package br.com.caelum.hibernatequerydsl

import org.hibernate.Session
import org.hibernate.cfg.Configuration
import org.junit.{Test, After, Before}
import org.junit.Assert._
import br.com.caelum.hibernatequerydsl.PimpedSession._
import br.com.caelum.hibernatequerydsl.TypeSafe._

class ActiveCollectionAcceptanceTest {

private var session:Session = _

@Before
def setUp {
val cfg = new Configuration();
session = cfg.configure().buildSessionFactory().openSession();
session.beginTransaction();
}

@After
def tearDown{
if (session != null && session.getTransaction().isActive()) {
session.getTransaction().rollback();
}
}

private def withUser(name:String=null,age:Int=0, street:String=null) = {
val user = new User
user setName name
user setAge age
session.save(user)
if(street!=null){
val address = new Address
address setStreet street
address setUser user
session.save(address)
}
this
}

private def and(name:String=null,age:Int=0, street:String=null) = {
withUser(name, age, street)
}

def ar = new ActiveCollection[User](null, session.from[User])

@Test
def shouldSupportTake {
withUser("guilherme").and("alberto")
val users = ar.take(1)
assertEquals("guilherme", users(0).getName)
assertEquals(1, users.size)
}

@Test
def shouldSupportParametersCombinedWithTake {
withUser("guilherme").and("alberto")
val users = ar.filter(_.getName equal "alberto").take(1)
assertEquals("alberto", users(0).getName)
assertEquals(1, users.size)
}

@Test
def shouldSupportGrabbingAll {
withUser("guilherme").and("alberto")
val users:List[User] = ar
assertEquals(2, users.size)
}

@Test
def shouldSupportRegrabbing {
withUser("guilherme").and("alberto")
val users = ar
assertEquals(2, users.size)
assertEquals(classOf[ActiveCollection[User]], ar.getClass)
assertEquals(1, users.take(1).size)
}

}
Expand Up @@ -10,6 +10,7 @@ import org.hibernate.criterion.Order._
import org.junit.{Test, Before, After}
import org.junit.Assert._
import scala.reflect.Code._
import br.com.caelum.hibernatequerydsl.TypeUnsafe._

class PimpedClassTest {

Expand Down
Expand Up @@ -5,6 +5,7 @@ import org.hibernate.cfg.Configuration
import org.junit.{Test, After, Before}
import org.junit.Assert._
import br.com.caelum.hibernatequerydsl.PimpedSession._
import br.com.caelum.hibernatequerydsl.TypeUnsafe._
class TypeSafeAcceptanceTest {

private var session:Session = _
Expand Down

0 comments on commit 7e51e7a

Please sign in to comment.