All entries for February 2006

February 26, 2006

Google talk rocks ;)

Writing about web page http://www.google.com/talk/

So I signed up my wife to gmail (or googlemail now) and it all went fine. Then I logged onto her gmail and she had so many more options than me, including google talk.

I downloaded it for my laptop and installed it and everything was cool, however I still couldn't see it via my email account.

Anyways, it turns out that changing my language settings to English (US) instead of English (UK) did the trick :)

Try it, it rocks :)


February 24, 2006

hsqldb + dates suck :(, but fixed in latest version :)

Writing about web page http://www.hsqldb.org/

So I was using a fairly old version of hsqldb and was doing some unit tests of my DAO which was selecting some rows based on a date column <= someDate.

Unforunately it behaved in the exact opposite way I was expecting :( If someDate was later than the value in the database column then you would expect dateColumn <= someDate to evaluate to true. But no, it didn't. It evaluated to false. Changing the condition to dateDolumn >= someDate evaluated to true!

And yes, timezones etc. were the same :)

Oh well, at least it was consistently wrong.

Upgrading to the latest hsqldb fixed it, and it was fun while it lasted :)


February 23, 2006

Eclipse allows multiple windows on same project

So I have two monitors side by side when I do development, and I typically want to have two windows on the same project (within Eclipse).

For example, I write a load of unit tests which fail, and then I write the code. This would be so much simpler if eclipse allowed me to have two windows, one on each monitor (i.e. one for unit tests, one for domain model).

And it does :)

If you go to window->New Window, it does exactly that. And updating a java file in one window updates the other window as you type :)

Very cool.


February 22, 2006

Spring Web Flow book is now released!

Writing about web page http://www.amazon.co.uk/exec/obidos/ASIN/159059584X/qid%3D1140629254/202-1067120-0416610

Yeah, so the new Spring Web Flow + Spring MVC book has finally been released :)

This makes me happy because I wrote the two chapters regarding Spring Web Flow, so go and buy a copy :) :)


February 21, 2006

hibernate executeUpdate + stale data

background

Whenever I develop applications I tend to do the simplest thing first and only "optimise" if needs be. So I had some logic which had to apply a Lock to a page and (optionally) all it's descendants.

So, the "lock application" logic should be seperated from the list of pages it is working on, so I introduced a LockAction which took a strategy for retrieving the pages that needed to be locked.

Fine, it all worked, but in terms of SQL it was extremely inefficient, executing one update for every page.

optimisation

Given that all the different strategies for selecting the pages to be locked could be expressed in SQL, an obvious optimisation is to execute a single SQL statement to update a number of pages, and then another SQL select to retrieve all the pages I had locked.

Fair enough, easy to do:


  Date dateLockExpires = new Date(System.currentTimeMillis() – lockThresholdInMs);
  String hql = "update " + AbstractPage.class.getName() + " p " +
                        " set p.lock.lockedBy=:user, p.lock.lockedSince=:lockDate" +
                        " where (" +
                        " p.lock.lockedBy is null" +
                        " or p.lock.lockedSince is null" +
                        " or p.lock.lockedBy = :user" +
                        " or p.lock.lockedSince <= :dateLockExpires" +
                        ") and ";
  hql += "p.url = :url";   // this is the strategy to select the set of pages
  Query query = session.createQuery(hql);
  query.setString("user", user.getUserId());
  query.setString("url", page.getUrl());
  query.setDate("dateLockExpires", dateLockExpires);
  query.setDate("lockDate", new Date());
  int numberUpdated = query.executeUpdate();

  String hqlForAllPages = "from " + AbstractPage.class.getName() + " p";
  hqlForAllPages += " where ";
  hqlForAllPages += "p.url ='" + page.getUrl() + "'";  // strategy to select the pages

  List lockedPages = new ArrayList();
  List unlockedPages = new ArrayList();
  List allPages = session.createQuery(hqlForAllPages).list();
  for (Page p: allPages) {
    Lock lock = p.getLock();
    if (lock == null || lock.getLockedBy() != user.getUserId()) {
      unlockedPages.add(p);
    } else {
      lockedPages.add(p);
    }
  }
  return new LockReportImpl(lockedPages, unlockedPages);

(ignore the horrible use of hql for the second select)
So basically we execute an update and then execute a select.

Simple? Yes. Works? No :( Unfortunately, even though the SQL statements are being sent to the database, and this all happens within the same transaction, the second select retrieves stale data, as if the previous executeUpdate() had never happened :(

One possible reason why is because we are using a version column, and the executeUpdate is not updating it. Modifying the update to increment the version column also does nothing :(

The only thing that works is to clear the entire session (session.clear()), but why should we have to do this? I am not asking Hibernate to execute raw SQL, it is executing hql, so why doesn't it do something sensible? I cannot expect it to know which rows have been updated, but I could (surely) expect it to invalidate all instances of that page from the first level cache?

Anyways, session.clear() works. Not sure what the impact will be on objects already loaded by that session…....


February 13, 2006

helloworld annotation

So I wanted to play around with Annotations, so I decided to do a hello world.

Actually, the use case is I wanted to mark hibernate backed objects as needing to be autowired by Spring.

I created my annotation

public @interface Autowired {
}

and updated my class:


@Autowired public class MyClass {
}

and nothing. The unit test failed:


        MyClass cf = new MyClass();

        Class class1 = cf.getClass();
//        assertTrue(class1.isAnnotationPresent(Autowired.class));
        Annotation[] anns = class1.getAnnotations();
        assertEquals(1, anns.length);

No idea why, thinking it might be a class path issue with Eclipse, I recompiled, but still nothing.

As it turns out, you need to annotate your annotations :) You need to tell the compiler how to retain the annotations (link).

Specifying that the annotations must be retained for runtime retrieval means it now works:


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

The Target BTW restricts this annotation so it can only be applied to a class.

Very useful stuff :)


February 08, 2006

CancellableFormController: about time :)

Writing about web page http://www.springframework.org/docs/api/org/springframework/web/servlet/mvc/CancellableFormController.html

Whenever I use Spring on a project there are a number of utilties that I end up writing time and time again (why don't I just copy them, or submit them to Spring; because I am a contractor and don't "own" the code I write :().

One of them is an extension to SimpleFormController which will understands a cancel request.

At long last ;) Spring has finally come up with their own implementation: link

Yeah!


Useful Spring tips

Writing about web page http://www.onjava.com/pub/a/onjava/2006/01/25/spring-xml-configuration-best-practices.html?page=1

Nothing too outstanding here, and I am not sure I agree with all of the tips, but a useful page for newbies :)

In particular, I disagree with not using autowiring; autowiring by type is safer than byName, and is very useful. Maybe I am making this point too strongly; I think it is fine to either use it or not.

The point I most strongly disagree with is preferring setter injection over constructor injection. That is just plain wrong ;) Constructors for collaborators which the class requires to work, setters for optional parameters. Simple as that.

But all in all, a nice quick read.


SQL; I love it

So despite using SQL for about 8 years now; I have never really got to grips with the intracies of it.

Anyways, I had to count the number of rows that contained a unique combination of two columns.

In this case, I needed to find all the html pages that were deleted, all the html pages that were undeleted, all the binary pages that were deleted and all the binary pages that were undeleted.

So after banging my head against a brick SQL wall for a few minutes, I resorted to code:


public final class JdbcStatisticsDAO extends JdbcDaoSupport implements StatisticsDAO {
    @SuppressWarnings("unchecked")
    public PageStatistics getPageStatistics(final Page page) {
        String sql = "select discriminator, is_deleted from page where ….";
        List rows = super.getJdbcTemplate().queryForList(sql);
        int deletedFiles=0;
        int files = 0;
        int deletedPages = 0;
        int pages = 0;

        for (Map row: rows) {
            // ARRGGHH!! Unfortunately Oracle comes back as bigDecimal, hsqldb uses boolean.
            boolean isDeleted = ConversionUtils.isTrue(row.get("IS_DELETED"));
            String discriminator = (String) row.get("DISCRIMINATOR");

            if ("html".equals(discriminator)) {
                if (isDeleted) {
                    deletedPages++;
                } else {
                    pages++;
                }
            } else {
                if (isDeleted) {
                    deletedFiles++;
                } else {
                    files++;
                }
            }
        }
        PageStatisticsImpl stats = new PageStatisticsImpl();
        stats.setRecursiveNoOfFilesExcludeDelete(files);
        stats.setRecursiveNoOfFilesIncludeDelete(deletedFiles);
        stats.setRecursiveNoOfPagesExcludeDelete(pages);
        stats.setRecursiveNoOfPagesIncludeDelete(deletedPages);
        return stats;
    }

Lovely; isn't it :)

Then I happened to chat to the Oracle guru that is Hongfeng :) who pointed out that I should use a combination of count and groupBy. A few minutes later and Hongfeng managed to find the exact page in one of the 5642 Oracle reference manuals.

This results in this:


    public PageStatistics getPageStatistics(final Page page) {
        String sql = "select discriminator, is_deleted, count(*) as theCount from page where page.url = '" + page.getUrl() + "' or page.url like '" + page.getUrlPath() + "%' group by discriminator, is_deleted";
        List rows = super.getJdbcTemplate(). queryForList(sql);
        int deletedFiles=0;
        int files = 0;
        int deletedPages = 0;
        int pages = 0;

        for (Map row: rows) {
            // ARRGGHH!! Unfortunately Oracle comes back as bigDecimal, hsqldb uses boolean.
            boolean isDeleted = ConversionUtils.isTrue(row.get("IS_DELETED"));
            String discriminator = (String) row.get("DISCRIMINATOR");
            int count = Integer.valueOf(row.get("theCount").toString());    // oracle bigInt

            if ("html".equals(discriminator)) {
                if (isDeleted) {
                    deletedPages = count;
                } else {
                    pages = count;
                }
            } else {
                if (isDeleted) {
                    deletedFiles = count;
                } else {
                    files = count;
                }
            }
        }
        PageStatisticsImpl stats = new PageStatisticsImpl();
        stats.setRecursiveNoOfFilesExcludeDelete(files);
        stats.setRecursiveNoOfFilesIncludeDelete(deletedFiles);
        stats.setRecursiveNoOfPagesExcludeDelete(pages);
        stats.setRecursiveNoOfPagesIncludeDelete(deletedPages);
        return stats;
    }

The code is still ugly as anything; but the SQL is cool. Much more efficient.

Cheers Hongfeng :)


So I wrote one myself.

Follow-up to Anyone know of a Composite proxy? from Colin's blog

So I wrote one myself:

link


February 2006

Mo Tu We Th Fr Sa Su
Jan |  Today  | Mar
      1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28               

Search this blog

Tags

Galleries

Most recent comments

  • Interesting… While I'm not completely convinced in such microbenchmarks, I'm pretty sure that 1ms … by Alexander Snaps on this entry
  • Hello. I bought the book yesterday. I was trying to find the source code for chapter 11 and chapter … by Suleman on this entry
  • http://woosight.net/account/login?username=demo by live mashup demo on this entry
  • Thanks mate ….. This blog was really helpful. by Maaz Hurzuk on this entry
  • Ty. Not directly helpful for my problem, but pointed me in the right direction. You will also get th… by Mike E. on this entry

Blog archive

Loading…
Not signed in
Sign in

Powered by BlogBuilder
© MMXX