Posts Tagged ‘tips’

Building Strings using Java MessageFormat

Monday, March 15th, 2010

Until I used SL4J, I didn't know that we can templatize String objects in a simple way. As most of us, use Log4J & apache commons logging framework for logging, we know general logging using these widely used open source frameworks. But there is more we can learn from these open source packages than just logging.

A simple debug in layman terms will be similar to writing System.out.println (String)

logger.debug("In "+location+", I said "+myMessage+" "+count+" times ");

to print "In Sunnyvale, I said Hello World! 7 times" in the log, if location="Sunnyvale", myMessage="Hello World!" and count=7

It is a simple debug statement which involves multiple String concatenation.

With SL4J, we can do the above debug like this

logger.debug("I said {} {} times ",myMessage, count);

which prints the same "I said Hello World! 7 times" in the log. Easy? huh!

After using it for a while, I thought this can be very well used for other string construction programs.

For eg, I need to make an API call to with various parameters which says userid=arun&location=sunnyvale&action=welcome

A common way of doing is :

StringBuilder sb = new StringBuilder();
sb.append("userid=").append(userid)
  .append("&location=").append(location)
  .append("&action=").append(action);

 doSomething(sb.toString());

SL4J's MessageFormatter

Using SL4J's MessageFormatter we can do

 doSomething(myFormat("userid={}&location={}&action={}", userid, location,action));

 private static String myFormat(String pattern, Object...argArray){
  return MessageFormatter.arrayFormat(pattern, argArray);
 }

I wish I can avoid my private method - myFormat, which automatically constructs array from varargs parameters.

Java MessageFormat

We already have MessageFormat from Sun(since JDK 1.4.2), which does exactly same & also supports varargs by default.

So we can do

 doSomething(
      MessageFormat.format("userid={0}&location={1}&action={2}", userid, location,action)
  );
 

There is a minor difference - we need to provide parameter number within {}, so it can pick the correct parameter to fill in flower braces. This is really useful for specific scenarios, which makes us not to worry about parameter order.

We can very well do,

 doSomething(MessageFormat.format("userid={2}&location={1}&action={0}", action,location,userid));
 

which constructs what I want.

MessageFormat is really powerful helper class for constructing String objects dynamically, without the overhead of String concatenation. It also keeps the code clean & concise. Typical use cases are internationalization (changing the text dynamically based on locale), General error message construction, String url Construction or anything in similar lines, where we know the countable set of variables to construct a string.

Use Timestamp instead of Date when results are bound by time – Hibernate SQLQuery

Thursday, November 12th, 2009

I was debugging a problem in our existing java application. It was ignoring the time while sorting. We use Hibernate to connect to underlying MySQL database.

Database

Entries sorted on Entry Date

Entries sorted on Entry Date

Java Code - Buggy

...
...
private final static String FIND_BY_BLOGGER =
    " SELECT t.entry_id AS entry_id FROM blog.blog_entries t " +
    " LEFT JOIN profile.profile p ON t.profile_id=p.profile_id  " +
    " WHERE p.login=:blogger AND t.entry_date < :toDate ORDER BY t.entry_date DESC " ;

public List<Long>
           findEntryIdsForBloggerId(final String userId, final int maxResults, Date toEntryDate)
{
    Session session = getSession();
    try {

        SQLQuery query = session.createSQLQuery(FIND_BY_BLOGGER);
        query.addScalar("entry_id",Hibernate.LONG);
        query.setString("blogger", userId);
        query.setDate("toDate", toEntryDate);
        query.setMaxResults(maxResults);

        return query.list();
    } finally {
        releaseSession(session);
    }
}
...
...

If we pass '2009-08-31 09:00:00', as toEntryDate, it was always missing the first(most recent) record - "Amp Up Your Look With StrangeBeautiful's Luxe Nail Lacquers".

Below database results mimic how the dao method retrieves the results.

Error results with setTime()

Error results with setTime()

After reviewing the code line by line, we figured query.setDate("toDate",toEntryDate) was the culprit.

Java Code - Corrected

...
...
private final static String FIND_BY_BLOGGER =
    " SELECT t.entry_id AS entry_id FROM blog.blog_entries t " +
    " LEFT JOIN profile.profile p ON t.profile_id=p.profile_id  " +
    " WHERE p.login=:blogger AND t.entry_date < :toDate ORDER BY t.entry_date DESC " ;

public List<Long>
           findEntryIdsForBloggerId(final String userId, final int maxResults, Date toEntryDate)
{
    Session session = getSession();
    try {

        SQLQuery query = session.createSQLQuery(FIND_BY_BLOGGER);
        query.addScalar("entry_id",Hibernate.LONG);
        query.setString("blogger", userId);
        query.setTimestamp("toDate", toEntryDate);
        query.setMaxResults(maxResults);

        return query.list();
    } finally {
        releaseSession(session);
    }
}
...
...

setDate() sets only the date portion of the toEntryDate in the query. We should use query.setTimestamp("toDate", toEntryDate) to set both date & time & get correct results.

SQLQuery.setDate() vs SQLQuery.setTimestamp()

SQLQuery.setDate() vs SQLQuery.setTimestamp()

After replacing it to setTimeStamp(), we started seeing all the records from said date & time!