alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Lift Framework example source code file (SquerylRecordSpec.scala)

This example Lift Framework source code file (SquerylRecordSpec.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Lift Framework tags/keywords

company, company, jdbc, mmm, name2, optimistic, outmapper, recordcomparer, sql, squerylrecord, stringexpression, stringexpression, t, t, text, transactionrollbackexception, transactionrollbackexception, util

The Lift Framework SquerylRecordSpec.scala source code

/*
 * Copyright 2010-2011 WorldWide Conferencing, LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.liftweb
package squerylrecord

import org.squeryl.Session
import org.squeryl.dsl.ast.FunctionNode
import org.squeryl.internals.OutMapper
import org.squeryl.dsl.StringExpression
import org.squeryl.dsl.DateExpression
import org.specs.Specification
import record.{BaseField, Record}
import record.field._
import RecordTypeMode._
import MySchema.{TestData => td, _}
import java.util.Calendar
import java.sql.Timestamp
import java.text.SimpleDateFormat


/**
 * Systems under specification for SquerylRecord.
 */
object SquerylRecordSpec extends Specification("SquerylRecord Specification") {

  doBeforeSpec {
    DBHelper.initSquerylRecordWithInMemoryDB()
    DBHelper.createSchema()
  }

  // NOTE: Use explicit forExample() in the examples to avoid
  // implicit ambiguity with Specs 1.6.6 in Scala 2.8.0
  "SquerylRecord" should {

    forExample("load record by ID") in {
      transaction {
        val company = companies.lookup(td.c2.id)
        checkCompaniesEqual(company.get, td.c2)

        val employee = employees.lookup(td.e1.id)
        checkEmployeesEqual(employee.get, td.e1)
      }
    }

    forExample("load record by string field value") in {
      transaction {
        val company = from(companies)(c =>
          where(c.name === td.c1.name.is) select (c))
        checkCompaniesEqual(company.single, td.c1)
      }
    }

    forExample("support order by") in {
      transaction {
        val orderedCompanies = from(companies)(c =>
          select(c) orderBy (c.name))
        val ids = orderedCompanies.map(_.id)
        // NOTE: This circumvents implicit conversion for the contents on List
        // ids must containInOrder(
        //   td.allCompanies.sortBy(_.name.is).map(_.id))
        ids.mkString("(", ",", ")") must_== td.allCompanies.sortBy(_.name.is).map(_.id).mkString("(", ",", ")")
      }
    }

    forExample("support normal joins") in {
      transaction {
        val companiesWithEmployees = from(companies, employees)((c, e) =>
          where(c.id === e.id)
            select ((c, e)))
        val ids = companiesWithEmployees.map(entry => (entry._1.id,
          entry._2.id))
        ids must haveSize(2)
        ids must containAll(List((td.c1.id, td.e1.id),
          (td.c2.id, td.e2.id)))
      }
    }

    forExample("support left outer joins") in {
      transaction {
        val companiesWithEmployees = join(companies, employees.leftOuter)((c, e) =>
          select(c, e)
          on(c.id === e.map(_.companyId))
        )

        companiesWithEmployees must haveSize(3)
        // One company doesn't have an employee, two have
        companiesWithEmployees.filter(ce => ce._2.isEmpty) must haveSize(1)

        val companiesAndEmployeesWithSameName = join(companies, employees.leftOuter)((c, e) =>
          groupBy(c.id)
          compute(countDistinct(e.map(_.id)))
          on(c.name === e.map(_.name))
        )

        // There are three companies
        companiesAndEmployeesWithSameName must haveSize(3)
        // One company has the same name as an employee, two don't
        companiesAndEmployeesWithSameName.filter(ce => ce.measures == 0) must haveSize(2)

        val employeesWithSameAdminSetting = join(employees, employees.leftOuter)((e1, e2) =>
          select(e1, e2)
          on(e1.admin === e2.map(_.admin))
        )

        // two employees, both have distinct admin settings
        employeesWithSameAdminSetting must haveSize(2)
        employeesWithSameAdminSetting.foreach { ee =>
          ee._2 must not (beEmpty)
          ee._1.id must_== ee._2.get.id
        }

        val companiesWithSameCreationDate = join(companies, companies.leftOuter)((c1, c2) =>
          select(c1, c2)
          on(c1.created === c2.map(_.created))
        )
        companiesWithSameCreationDate must not (beEmpty)

        val employeesWithSameDepartmentNumber = join(employees, employees.leftOuter)((e1, e2) =>
          select(e1, e2)
          on(e1.departmentNumber === e2.map(_.departmentNumber))
        )
        employeesWithSameDepartmentNumber must not (beEmpty)

        val employeesWithSameRoles = join(employees, employees.leftOuter)((e1, e2) =>
          select(e1, e2)
          on(e1.role === e2.map(_.role))
        )
        employeesWithSameRoles must not (beEmpty)
      }
    }

    forExample("support one to many relations") in {
      transaction {
        val company = companies.lookup(td.c1.id)
        company must beSome[Company]
        val employees = company.get.employees
        employees must haveSize(1)
        checkEmployeesEqual(td.e1, employees.head)
      }
    }

    forExample("support many to many relations") in {
      transactionWithRollback {
        td.e1.rooms must haveSize(2)

        td.e2.rooms must beEmpty

        td.r1.employees must haveSize(1)
        td.r3.employees must beEmpty

        td.r3.employees.associate(td.e2)
        td.e2.rooms must haveSize(1)
      }
    }

    forExample("support updates") in {
      val id = td.c1.id

      transactionWithRollback {
        val company = companies.lookup(id).get
        company.name("New Name")
        company.postCode("11111")
        companies.update(company)

        val loaded = companies.lookup(id).get
        checkCompaniesEqual(company, loaded)

        update(companies)(c => where(c.id === id)
          set (c.name := "Name2"))
        val afterPartialUpdate = companies.lookup(id).get
        afterPartialUpdate.name.is must_== "Name2"
      }

      // After rollback, the company should still be the same:
      transaction {
        val company = companies.lookup(id).get
        checkCompaniesEqual(td.c1, company)
      }
    }

    forExample("support delete") in {
      transactionWithRollback {
        employees.delete(td.e2.id)
        employees.lookup(td.e2.id) must beNone
      }
    }

    forExample("support select with properties of formerly fetched objects") in {
      transaction {
        val company = companies.lookup(td.c2.id).head
        val employee = from(employees)(e =>
          where (e.companyId === company.idField) select(e)).head
        employee.id must_== td.e2.id

        val loadedCompanies = from(companies)(c =>
          where (c.created === company.created) select(c))
        loadedCompanies.size must beGreaterThanOrEqualTo(1)
      }
    }

    forExample("support associate with one-to-many relations") >> {
      transactionWithRollback {
        //td.c3.employees.associate(td.e2)
        //td.e2.company.id must_== td.c3.id
      }
    }
    
    forExample("support many to many relations") >> {
      transactionWithRollback {
        td.e1.rooms must haveSize(2)
      }
    }

    forExample("support date/time queries") >> {
      transaction {
	val c1 = from(companies)(c =>
	  where (c.created <= Calendar.getInstance)
	  select(c))
	c1.size must beGreaterThan(1)

	val c2 = from(companies)(c =>
	  where (c.created <= Calendar.getInstance.getTime)
	  select(c))
	c2.size must beGreaterThan(1)
      }
    }

    forExample("support inner queries") >> {
      import record.field._

      transaction {
        // Should work with the ID function (returns a long):
        val companyId: Long = from(companies)(c => where(c.id in 
            from(companies)(c2 => where(c2.id === td.c1.id) select(c2.id))) 
            select(c.id)).single
        companyId must_== td.c1.id
        
        // It should also be possible to select the ID field directly:
        val companyIdField: LongField[Company] = from(companies)(c => where(c.idField in 
            from(companies)(c2 => where(c2.id === td.c1.id) select(c2.idField))) 
            select(c.idField)).single
        companyIdField.is must_== td.c1.id
        
        // Strings should also be selectable in inner queries
        val companyIdByName: Long = from(companies)(c => where(c.name in 
            from(companies)(c2 => where(c2.name === td.c1.name) select(c2.name)))
            select(c.id)).single
        companyIdByName must_== td.c1.id
        
        // ...And DateTime-Fields:
        val companyIdByCreated: DateTimeField[Company] = from(companies)(c => where(c.created in 
            from(companies)(c2 => where(c2.id === td.c1.id) select(c2.created)))
            select(c.created)).single
        companyIdByCreated.is must_== td.c1.created.is
        
        // Decimal Fields:
        val empSalary: DecimalField[Employee] = from(employees)(e => where (e.salary in
            from(employees)(e2 => where(e2.id === td.e1.id) select(e2.salary)))
            select(e.salary)).single
        empSalary.is must_== td.e1.salary.is
           
        // Email fields:
        val empEmail: EmailField[Employee] = from(employees)(e => where (e.email in
            from(employees)(e2 => where(e2.id === td.e1.id) select(e2.email)))
            select(e.email)).single
        empSalary.is must_== td.e1.salary.is
        
        // Boolean fields:
        val empAdmin: BooleanField[Employee] = from(employees)(e => where (e.admin in
            from(employees)(e2 => where(e2.id === td.e1.id) select(e2.admin)))
            select(e.admin)).single
        empAdmin.is must_== td.e1.admin.is
        
        // Enum fields:
        val empRole: EnumNameField[_, _] = from(employees)(e => where (e.role in
            from(employees)(e2 => where(e2.id === td.e1.id) select(e2.role)))
            select(e.role)).single
        empRole.is must_== td.e1.role.is 
        
      }
    }

  forExample("support the CRUDify trait") >>  {
	  transaction{
		  val company = Company.create.name("CRUDify Company").
		  created(Calendar.getInstance()).
		  country(Countries.USA).postCode("90210")
		  val bridge = Company.buildBridge(company)
		  bridge.save
		  val id = company.id
		  company.isPersisted must_== true
		  id must be_>(0l)
		  company.postCode("10001")
		  bridge.save
		  val company2 = Company.findForParam(id.toString)
		  company2.isDefined must_== true
		  company2.foreach(c2 => {
			  c2.postCode.get must_== "10001"
		  })
		  val allCompanies = Company.findForList(0, 1000)
		  allCompanies.size must be_>(0)
		  bridge.delete_!
		  val allCompanies2 = Company.findForList(0, 1000)
		  allCompanies2.size must_== (allCompanies.size - 1)
	  }
  }
  
  forExample("Support Optimistic Locking") >> {
	  val company = Company.create.name("Optimistic Company").
	  	created(Calendar.getInstance()).
	  	country(Countries.USA).
	  	postCode("90210")
	  //First insert the company in one transaction	
	  transaction{
		  companies.insert(company)
	  }
	  //Retrieve and modify in another transaction
	  val innerUpdate = new Thread(new Runnable{
		  override def run(){
			  transaction{
				  val company2 = companies.lookup(company.id).get
				  company2.created(Calendar.getInstance())
				  companies.update(company2)
			  }
		  }
	  })
	  innerUpdate.start
	  innerUpdate.join
	  //Then in a third transaction, try to update the original object
	  transaction{
		  import org.squeryl.StaleUpdateException
		  company.created(Calendar.getInstance())
		  companies.update(company) must throwAn[StaleUpdateException]
	  }
  }
  
  forExample("Allow custom functions") in {
    inTransaction{
	    val created =
	      from(companies)(c =>
	        where(c.name === "First Company USA")
	        select(&(toChar(c.created,"EEE, d MMM yyyy")))
	      )
	    created.head must_== new SimpleDateFormat("EEE, d MMM yyyy").format(Calendar.getInstance().getTime())
    }
  }

  }
  
  class ToChar(d: DateExpression[Timestamp],e: StringExpression[String], m: OutMapper[String]) 
  		extends FunctionNode[String]("FORMATDATETIME",Some(m), Seq(d,e)) with StringExpression[String]

  def toChar(d: DateExpression[Timestamp],e: StringExpression[String])(implicit m: OutMapper[String]) = new ToChar(d,e,m)

  class TransactionRollbackException extends RuntimeException

  /**
   * Runs the given code in a transaction and rolls
   * back the transaction afterwards.
   */
  private def transactionWithRollback[T](code: => T): T = {
    var result: T = null.asInstanceOf[T]
    try {
      transaction {
        result = code
        throw new TransactionRollbackException()
      }
    } catch {
      case e: TransactionRollbackException => // OK, was rolled back
    }

    result
  }

  private def checkCompaniesEqual(c1: Company, c2: Company) {
    val cmp = new RecordComparer[Company](c1, c2)
    cmp.check(_.idField)
    cmp.check(_.description)
    cmp.check(_.country)
    cmp.check(_.postCode)
    cmp.check(_.created)

    cmp.checkXHtml()
  }

  private def checkEmployeesEqual(e1: Employee, e2: Employee) {
    val cmp = new RecordComparer[Employee](e1, e2)
    cmp.check(_.name)
    cmp.check(_.companyId)
    cmp.check(_.email)
    cmp.check(_.salary)
    cmp.check(_.locale)
    cmp.check(_.timeZone)
    //cmp.check(_.password)
    cmp.check(_.admin)
    cmp.check(_.departmentNumber)
    cmp.check(_.role)

    // Photo must be checked separately
    e1.photo.is match {
      case Some(p) => {
        val p2 = e2.photo.is
        p2 must beSome[Array[Byte]]
        p2.get.size must_== p.size

        (0 until p.size).foreach { i =>
          p2.get(i) must_== p(i)
        }
      }
      case None => e2.photo.is must beNone
    }
  }

  class RecordComparer[T <: Record[T]](val r1: T, val r2: T) {
    def check(fieldExtractor: (T) => BaseField) {
      val f1 = fieldExtractor(r1)
      val f2 = fieldExtractor(r2)
      f1.get must_== f2.get
      f1.name must_== f2.name
    }

    def checkXHtml() {
      r1.toXHtml must_== r2.toXHtml
    }
  }
}

Other Lift Framework examples (source code examples)

Here is a short list of links related to this Lift Framework SquerylRecordSpec.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.