Archive for December, 2009

Building Multi-Criteria Search Queries in Hibernate

December 9th, 2009

In this post I am going to show how to write queries multi-criteria search screens. There are two approaches for making this possible.

  • HQL for building the Query
  • Building Query using Criteria API

HQL for building the Query

Here I am going to show 2 approaches to building the HQL and try to point out the better approach.

Approach I:String concatenation
This approach uses String concatenation and setting up the values directly in the query.

if (startDate != null) {
   if (firstClause) {
      query = query + " where ";
   }
   else {
      query = query + " and ";
   }
 
   query += " s.date >= '" + startDate + "'";
}

Using the above approach there might be a chance of SQL Injection attack and using string concatenation is inherently error-prone.

Approach 2: Criteria as Named Parameters

In this one we create two map to hold parameter name and value which could be binded to the HQL during the execution.

Here is brief example of the approach.

public List search() {
   StringBuilder aQuery = 
      new StringBuilder(" from document p where p.id is not null ");
   HashMap parameterMap = new HashMap();
   HashMap parameterListMap =  new HashMap();
 
   //Collection Criteria
   if (countyList != null) {
      buildCollectionCriterion(aQuery, parameterListMap, "listID", countyList);
   }
 
   //Date Criteria
   if (startDate!= null || endDate != null) {
      buildDateCriterion(aQuery, parameterMap, "dateField",startDate, endDate);
   }
 
   Query query = hibSession.createQuery(aQuery.toString());
 
   for (String key : parameterMap.keySet()) {
      query.setParameter(key, parameterMap.get(key));
   }
 
   for (String key : parameterListMap.keySet()) {
      query.setParameterList(key, parameterListMap.get(key));
   }
 
   List results = query.list();
}
 
//Helper Methods for different type of criteria
 
protected void buildCollectionCriterion(StringBuilder aQuery, 
                                        Map parameterListMap, 
                                        String aFieldName, 
                                        Collection aList) {
   if (aList != null && !aList.isEmpty()) {
      aQuery
         .append(" and p.")
         .append(aFieldName)
         .append(" in (:")
         .append(aFieldName)
         .append(")");
      parameterListMap.put(aFieldName, aList);
   }
}
 
public void buildDateCriterion(StringBuilder aQuery, 
                               Map parameterMap, 
                               String aFieldName, 
                               Date aStartDate, 
                               Date anEndDate) {
   if (aStartDate != null && anEndDate != null) {
      aQuery
         .append(" and ( p."
         .append(aFieldName)
         .append(" between :aStartDate and :anEndDate)");
      parameterMap.put("aStartDate", aStartDate);
      parameterMap.put("anEndDate", anEndDate);
   }
   else if (aStartDate != null) {
      aQuery
         .append(" and  (p.")
         .append(aFieldName)
         .append(" >= :aStartDate)");
      parameterMap.put("aStartDate", aStartDate);
   } 
   else if (anEndDate != null) {
      aQuery
         .append(" and (p.")
         .append(aFieldName)
         .append(" <=:anEndDate");
      parameterMap.put("anEndDate", anEndDate);
   }
}

Building Query using Criteria API

public List search() {
        Criteria c = hibSession.createCriteria(Document.class);
        c.add(Restrictions.notNull("id"));
 
        if(countryList != null) {
                c.add(Restrictions.in("listId", countryList));
        }
 
        if(startDate != null) {
                c.add(Restrictions.ge("dateField", startDate);
        }
 
        if(endDate != null) {
                c.add(Restrictions.le("dateField", endDate);
        }
 
        return c.list();
}

Conclusion

The Hibernate Criteria API is a powerful and elegant library which is well adapted for implementing multi-criteria search functionality and also HQL queries must be built ‘on-the-fly’. Using it in appropriate circumstances will result in cleaner, clearer, more reliable and more maintainable code.

Word Counts Example in Ruby and Scala

December 8th, 2009

A while ago I was asked, as a pre-interview task for another company, to write some code in any language that counted word frequencies in these newsgroup articles. I recently came across the Ruby and Scala scripts I wrote and thought it would be fun to post them.

First, here is the wordcounts.rb script. It assumes the newsgroup files have already been downloaded and is executed by running ruby wordcounts.rb.

#Change rootDir to the location of your newsgroup files
rootDir = "/home/zcox/dev/20_newsgroups"
raise rootDir + " does not exist" unless File.directory? rootDir
 
#Iterates over all files under rootDir, opens each one and passes it to the block
def files(rootDir)
  Dir.foreach(rootDir) do |dir|
    if dir != "." && dir != ".."
      puts "Processing " + dir
      Dir.foreach(rootDir + "/" + dir) do |file|
        if file != "." && file != ".."
          open(rootDir + "/" + dir + "/" + file) do |f|
            yield(f)
          end
        end
      end
    end
  end
end
 
t1 = Time.now
counts = Hash.new(0) #0 will be the default value for non-existent keys
files(rootDir) do |file|
  file.read.scan(/\w+/) { |word| counts[word.downcase] += 1 }
end
 
puts "Writing counts in decreasing order"
open("counts-descreasing-ruby", "w") do |out|
  counts.sort { |a, b| b[1] <=> a[1] }.each { |pair| out << "#{pair[0]}\t#{pair[1]}\n" }
end
 
puts "Writing counts in alphabetical order"
open("counts-alphabetical-ruby", "w") do |out|
  counts.sort { |a, b| a[0] <=> b[0] }.each { |pair| out << "#{pair[0]}\t#{pair[1]}\n" }
end
 
t2 = Time.now
puts "Finished in " + (t2 - t1).to_s + " seconds"

The code just iterates over each file, splits each file into words and aggregates the word counts using a Hash object. It then sorts the Hash in two different ways and writes each to a separate file. I think Ruby makes this code very easy to read and Ruby’s core library support makes this task pretty simple to accomplish.

Next, here is the wordcounts.scala version. Again it assumes the newsgroup articles are already downloaded and can be run by executing scala wordcounts.scala.

//Change rootDir to the location of your newsgroup files
import java.io._
val rootDir = new File("/home/zcox/dev/20_newsgroups")
if (!rootDir.exists) throw new IllegalArgumentException(rootDir + " does not exist")
 
/** Iterates over all files under rootDir, opens each one and passes it to the function */
def files(rootDir: File)(process: File => Unit) {
  for (dir <- rootDir.listFiles; if dir.isDirectory) {
    println("Processing" + dir)
    for (file <- dir.listFiles; if file.isFile) {
      process(file)
    }
  }
}
 
val t1 = System.currentTimeMillis
var counts = Map.empty[String, Int].withDefaultValue(0)
files(rootDir) { file => 
  file.split("""\W+""").foreach { word => counts = counts(word.toLowerCase) += 1 }
}
 
println("Writing counts in decreasing order")
write(counts, "counts-descreasing-scala") {_._2 > _._2}
 
println("Writing counts in alphabetical order")
write(counts, "counts-alphabetical-scala") {_._1 < _._1}
 
val t2 = System.currentTimeMillis
println("Finished in " + ((t2 - t1)/1000.0) + " seconds");
 
/** Writes the specified map to the specified file in tab-delimited format, sorted accordingly. */
def write[K, V](map: Map[K, V], file: String)(sort: (Tuple2[K, V], Tuple2[K, V]) => Boolean) {
  using (new PrintWriter(new FileWriter(file))) { out => 
    map.toList.sort(sort).foreach { pair => out.println(pair._1 + "\t" + pair._2) }
  }
}
 
/** Converts a File to a String. */
implicit def file2String(file: File): String = {
  val builder = new StringBuilder
  using (new BufferedReader(new FileReader(file))) { reader => 
    var line = reader.readLine
    while (line != null) {
      builder.append(line).append('\n')
      line = reader.readLine
    }
  }
  builder.toString
}
 
/** Performs some operation on the specified closeable object and then ensures it gets closed. */
def using[Closeable <: {def close(): Unit}, B](closeable: Closeable)(getB: Closeable => B): B = 
  try {
    getB(closeable)
  } finally {
    closeable.close()
  }

Notice that Scala code is statically typed and compiled software that can be run as a simple script – Scala really does scale from small one-off scripts to large enterprise systems. It mirrors the Ruby script fairly closely, with the addition of some helper functions at the end to make up for some features that the core Scala library doesn’t provide. Overall I think it’s just as readable as the Ruby code, but comes with static type-checking and it runs on the JVM.

I have included some timing info as well. On my 2GHz Core2 Duo with 3GB RAM, the Ruby script averages about 60 seconds while the Scala script averages about 30 seconds. The Scala version uses immutable Map objects to store the word counts; thus a new Map object is created for each word. I switched it to a mutable Map (which required only minor code changes) and it dropped to about 15 seconds. Hardly scientific, I’m sure a lot of optimizations could be done on both scripts, but this shows the performance gains provided that can be achieved on the JVM using Scala.

If anyone can spot any improvements to make to these scripts, please share them with comments!

Windows Black Screen Of Death

December 4th, 2009

It seems the Black Screen of Death has struck again. Microsoft isn’t confirming or denying that the issue was caused by a Microsoft update, but this issue is not new to Windows users. It affects all breeds of Windows OS, after logging in the screen goes black. Some users are able to bring up the task manager by doing a ctrl + alt + delete. Prevx (A security company in the UK) has provided a possible fix. Prevx released a statement about this issue:

“The cause of this recent crop of Black Screen appears to be a change in the Windows Operating System’s lockdown of registry keys,” wrote Kennerly. “This change has the effect of invalidating several key registry entries if they are updated without consideration of the new ACL rules being applied. For reference the rule change does not appear to have been publicized adequately, if at all, with the recent Windows updates.”

Here are the recommended steps

  • Restart
  • Log on and wait for the black screen to appear
  • Make sure your PC should be able to connect to the Internet (black screen does not appear to affect this)
  • Press the CTRL, ALT and DEL keys simultaneously
  • When prompted, Click Start Task Manager
  • In Task Manager Click on the Application Tab
  • Next Click New Task
  • Now enter the command:
  • “C:\Program Files\Internet Explorer\iexplore.exe” “http://info.prevx.com/download.asp?GRAB=BLACKSCREENFIX”  (The file has a code certificate backed by VeriSign.)
  • Click OK and your (Web) browser should start up and begin the download process
  • When prompted for the download Click run, the black screen fix program will download and run to automatically fix the issue.
  • Now restart your PC and the black screen problem will hopefully be gone.

You have a 1 in 10 chance that this will actually work.

For all the positives that updates have to offer the truth remains that the results can sometimes be undesirable. Security patching is a must and I would never recommend not performing updates. The wise engineer will image his or her disk or make some other form of reliable backup before performing updates. Unfortunately as shown with this issue, the negative effects may take a couple of weeks to surface, and in a production environment rolling back to an image is not an option.