Why Writing a Blogging Engine is a Waste of Time

Cal Henderson (of Flickr fame) gave a keynnote address at DjangoCon 2008 titled "Why I Hate Django" that is worth watching for many reasons. At one point during the talk he asks the audience to put up their hands if they were building a blogging engine. Nearly everyone did, which elicited the response to someone who wasn’t “Why aren’t you?”

I don’t really understand this need. Well, I do sort of understand. Most programmers have pet projects they’ve been working on, sometimes for years. The common thread with most of these is that they will never be finished. Starting things is easy and fun. Finishing things is hard and often painful.

Last month I decided to share my thoughts on starting a programming blog, which was generated a lot of comments on reddit. There were such comments as:

It isn't a programming blog unless you write the platform yourself :)

This is a common sentiment. Spurred on by the success of Wordpresss, programmers seem particularly predisposed to believe they can produce something better, which is why there is no shortage of Google killers and failed social networking sites

You see this attitude at the code level too. There is a tendency for programmers to constantly rewrite things because “next time it will be better”. I’ve certainly suffered from this malaise—and believe me, it is a malaise—but I guess perfectionism is part of the programmer package.

The other part of this is the Not Invented Here syndrome. Programmers almost universally prefer code they wrote over code they didn’t. After all, you understand what you wrote. what’s more, it coincidentally behaves exactly like you’d want it to (or, rather, is has the promise to once you put a whole lot more time into it, time that never seems to materialize).

Apart from these obvious and universal reasons, I see several distinct motivations:

  1. To learn something new or just for fun;
  2. To compete with Wordpress or Blogger; and
  3. To use for one’s own blog.

Now I have no issue with (1). Code exercises are the best form of programming-related learning.

(2) can probably be ascribed to either extreme naiveté or hubris. As the list of Google killers and failed social networking sites should tell you: chasing the leader is usually a recipe for failure. As Zed Shaw said:

If you tell me that your social network will take on facebook because it includes baby pictures then I’m going to laugh in your face. They are an established player with CIA backing. You won’t wipe them out.

As incendiary as Zed Shaw was (and is) and as tongue-in-cheek as the above comment is, he’s right. A venture capitalist I met once said something along the lines of twice as good isn’t good enough. To be even considered a worthwhile risk, something has to be an order of magnitude better, faster or cheaper.

Fact is, the blogging problem is solved. That doesn’t mean you can’t do it better. It just means we’ve reached the point where it no longer matters.

This brings me to (3), the real crux of my argument. Time spent writing a blogging engine when you are a blogger (or aspire to be one) is time not spent blogging (or something else productive for that matter). But how much time is that?

The minimalists will argue not much at all. Take this comment:

The only real requirement to start a programming blog is to start generating content. It doesn't matter how you publish that content, but what your content is. You can use freaking plain text files.

While I agree content is king, I have to disagree with the rest. Where once the above might’ve feasible—even common—the world has changed. Blogging is a crowded space. Writing for its own sake can be rewarding and one can certainly hope that through some good fortune and grass roots growth, such a blog might one day be read by thousands or even millions. this is largely idealistic, anachronistic, pie-in-the-sky nonsense.

A modern blogging engine does an awful lot these days, including:

  • Allowing designers to create templates that can be plugged in. At the risk of offending such minimalist plain-text purists but you’re living in the past. Presentation matters. There is a reason the Web isn’t just hyperlinked plain text (any more);
  • creating navigation;
  • creating site maps for search engines;
  • allows commenting with protection from spamming;
  • has syndication (ie an RSS or Atom feed);
  • integrates with distribution platforms (eg Technorati). Distribution matters;
  • allows third-party widgets (although personally I think this is of little value beyond the standard ones);
  • and so on.

Now sure you could do each and every one of these things yourself (and more) but why would you bother trying to reinvent this particular wheel? Can you live without these features? Maybe, but why would you?

You should write a blog because you’ve got something to say. Doing so just because you think you might be able to make some money or make a name for yourself will lead you down the road to frustration , uninspired writing and probably failure. But I can tell you from experience that writing stuff people read is way more fun than writing things people don’t read.

I wrote Do Programmers Optimize... Life? because it had been on my mind and I got a kick out of (hopefully) providing an insight into my mental process (particularly to non-programmers I know) but thanks almost entirely to programming.reddit.com, over 13,000 have read it (according to Google Analytics) to date. I suspect—but can't confirm—that it garnered enough up-votes to make it onto at least the second page of the main reddit site or possibly the first.

And while this could’ve been written in raw ASCII, If your idea is worth spreading, then presentation matters. Sir Ken Robinson said:

Professors look at their bodies as a form of transport for their heads.

If you haven’t gotten it yet, presentation matters. A blog is not just an HTTP delivery mechanism for raw text. Your goal is to engage the reader.

Like Steve Yegge, “I'd love to read your blog”. If you’re going to write something then write something. Don’t waste your time figuring out how to implement RSS, hook into OpenID, stop spam with CAPTCHA and so on. If that’s your equivalent of horsing around in the tool shed for relaxation then knock yourself out but not at the expensive of writing something.

Do Programmers Optimize... Life?

Back in university, a friend who was studying arts and I were talking and at one point I said something like "I see every situation as a problem requiring optimization", which elicited the response "How... sad".

Now I was probably no more than 19 at the time. By that stage I'd been programming for nearly 10 years, having started out on a Commodire VIC 20 (and yes I'm still bitter we didn't get a Commodore 64) then graduating several years later to the dizzy heights of an IBM PC-XT (complete with CGA graphics). That eventually got me onto Turbo Pascal, which marked a turning point.

That being said, even until the end of high school, it actually hadn't occurred to me that being a computer programmer was even a job, let alone that I should be one. I wasn't fast-tracking or jump-starting myself into a career. I just liked doing it and it had basically replaced that childhood stalwart of Lego. The two I think actually have a lot in common except programming is a far more tractable medium.

As it happened, I went to university not to become a programmer. In fact, I wasn't sure what I wanted to do yet. I just felt like I should go or even that it was expected (i was the first to go in my father's line). Interestingly, I had always had far more natural ability with mathematics.

During this period, a lot of what I now consider to be key indicators for a programmer-to-be were there: game-playing, basically. All sorts of games: computer games, card games, role-playing games. It borders on being a cliché. All of this relates to an affinity for problem solving, a penchant for analytical and rational thinking and an ability to conceptualize abstractions—all key abilities and qualities of a programmer.

This of course raises the age-old chicken-and-egg question of: do people become programmers because they have the right mindset or do they develop that mindset as a consequence of becoming computer programmers?

The truth, as always, probably lies somewhere in the middle (yes there is something between 1 and 0). It should be noted that people don't just magically have an analytical mindset. In spite of whatever natural predilections someone might have, a disciplined mind is something that is developed.

The question I'm getting to is: how much of this changes the way your mind operates? Learning analytical and problem solving skills for programming is one thing, But what about everyday life?

Let me give you an example.

Sometimes I'll walk home from work. This is a fairly long walk at about 5.8km (3.6mi). Pretty precise distance right? It probably won't surprise you to know I measured it with a GPS. Now like most reasonably modern cities, the layout is largely grid-based. The path to my house is not a direct line by road or walkway although it's pretty close. But I am "off" several blocks.

So immediately we have two problems:

  1. There are a number of different paths going a combination of horizontally and vertically with the requirement that at some point a given number of horizontal moves need to be made (assuming the longer distance is vertical in this relative frame of reference; and
  2. Any diagonal shortcuts are big potential time-savers as they, unlike any horizontal or vertical moves, can reduce the total distance needed to travel.

But it gets more complicated than that. Every road that needs to be crossed can be avoided (by changing route or using an overpass or underpass), crossed at a set of traffic lights or crossed where there are no lights. The traffic lights are significant because they will have timed pedestrian crossings. These may take longer to come up but are guaranteed to come up within a certain time period. With no lights on a particularly busy street you might be waiting awhile.

Sound familiar? In algorithmic terms, we have the classic (and common) choice between a deterministic and a probabilistic solution. So we can profile our street crossings:

Overpass/Underpass Traffic Lights No Traffic Lights
Best Case 0 seconds 0 seconds 0 seconds
Expected Case 0 seconds 60 seconds1 Varies2
Worst Case 0 seconds 150 seconds3 Varies2
1. Based on (3) and the possibility of an early crossing if traffic is sufficiently light
2. Each example needs to be considered independently and can vary by weather, whether it's school holidays or not, time of day and other factors
3. Based on experience of average cycle length of relevant traffic lights.

But wait, there's more! Pedestrian congestion needs to be considered. That bridge over Wellington St might be a convenient way to avoid a busy road and be out of the rain but it also has an awful lot of people on it going (at this time of day) to the train station.

Add to this you may need to vary the route for, say, trips to the supermarket, post office, an ATM, the butcher and so on.

Pretty full on, right?

Now I would imagine that if you're not a programmer—and not in IT altogether—your reaction to above is somewhere between bewilderment and horror. But if you are a programmer I'd say there's a pretty reasonable chance that you're not that surprised.

Before you write me off as suffering form severe OCD, perhaps this will allow you to downgrade it to, well, mild OCD. In this (excellent) movie Spy Game Nathan Muir (Robert Redford) is teaching Tom Bishop (Brad Pitt) spycraft. In a restaurant Muir says:

Muir:
Every building, every room, every situation is a snap shot. I'm sitting here talking to you, I'm also checking the room-memorizing it-the people, what their wearing, then I ask the question-what's wrong with this picture? Anything suspect? You gotta see it, assess it and dismiss it - most of it without looking, without thinking.
Bishop:
Without thinking?
Muir:
It's just like breathing. You breathe don't you?

My point is that the above scenario of optimizing the walk home—which I assure you has happened—is not something that I actually put much thought into or any preplanning. Like Muir, it's a reaction as automatic as breathing.

So am I hugely atypical (for a programmer)? Did programming make me this way? Did I become this way because of programming? I'm hoping the answers are "no", "somewhat" and "somewhat" but who can say for sure? Perhaps I'm just an allegedly functional savant?

I will say that I would be honestly surprised if the higher-order thought processes required to be a good programmer didn't leak into non-programming areas. If it didn't I think I'd find that level of compartmentalization a little disturbing.

The Hysteria over URL Shortening

If you keep up with current events on the internet at large and the Web in particular, it's easy to become convinced that the World (Wide Web) is coming to an end. Both the good and the bad are exaggerated. Just as Opera hasn't reinvented the Web, URL shortening isn't the end.

For those who are (somehow) unfamiliar with URL shortening, it is the process of taking a long URL and turning it into a short one. Certain sites exist to do this, most notably Tiny URL but there are many others. So instead of http://www.cforcoding.com/2009/06/ibatis-tutorial-dynamic-sql.html I can have http://tinyurl.com/klvwuc.

The mechanism for doing this is simple: your browser goes to the shortened URL. That site simply does an HTTP redirect back to the original URL.

Although URL shortening predates Twitter, Twitter is the most common reason. Tweets are limited to 140 characters so every character counts. It's not the only reason however. For example, StackOverflow can't autolink URLs containing parentheses.

The main criticism of URL shortening is that it exacerbates link rot. Not only can the target change but the URL shortening service itself can go offline and I guarantee you that will happen.

The other major criticism of URL shortening is that it will break the Web by breaking search engines. To understand this argument, you simply need a high-level understanding of how search engines such as Google work. They look at the content of pages and create indexes. They crawl the links on those pages to find other pages. By virtue of the content of your page, what links there are to your page, how long your domain has been registered and a number of other factors, Google assigns your page an authority (commonly referred to as "PageRank"). Google uses that PageRank as a key component of ordering search results.

Some argue that if URL shortening reaches some critical mass, the links between pages will devolve to the point where search engines can't rank pages.

This may sound familiar as some once feared that because search engines (meaning "Google") were so good that noone would link to pages anymore and Google would become a victim of its own success (for the same reason: lack of links).

Like I said, people love to be dread merchants. I guess it makes for a good headline. But, as just one example, the history of predicting Google's demise has been hysterically funny to date.

The latest to weigh into this debate is Jeff Atwood, largely quoting Joshua Schacter who decries the extra layer of indirection as well as doom-and-gloom scenarios such as erasing a database and monetization of these links.

While I love a good "Chicken Little" story as much as the next reader, one has to point out that none of this matters.

While there are some valid criticisms of URL shortening such as the idea that it should be part of Twitter (to avoid the extra point-of-failure), the rest is largely just a storm in a teacup.

To illustrate my point, if you google "ibatis tutorial" and your results are like mine, you'll see two links on this first page pointing to posts I've written. One of them is a link to a DZone submission of one of those articles.

The astute reader will be able to view source on this page and find not a single reference to my blog on that page. So why does Google rank this page similarly? For those unfamiliar with DZone, it is a social news site for submitting programming related articles blog posts, tutorials, news and the like. Clicking on one of the links will go back to DZone, which will then redirect you to the page. They do this to measure click-through rate, which is the exact same technique used in Web advertising and URL shortening.

The salient points to take from this observation is that Google is smart enough to follow a redirect and I guarantee you that Google has indexed every single shortened URL it has ever found. Google understands HTTP redirects.

That debunks the search problem but what about link rot and monetization? If ever the problem becomes sufficiently large then the worst possible outcome is Google will need to reluanch such a site, provide that site with a backup of all their links or simply provide you with a plugin (like the Google toolbar) to automatically redirect you to the correct place.

That's all.

So don't panic. It's not the end of the Web. It's not the beginning of the Web. Nor is it the thin end of the wedge of the slipper slope. This is something that if it ever does become a problem is trivial to solve. So no, the sky isn't falling.

Ibatis Tutorial: Dynamic SQL

This tutorial expands on the knowledge and sample code from Ibatis Tutorial: Aggregation with groupBy and previous Ibatis tutorials.

One of the problems in dealing with database queries is that the SQL needs to change based on runtime conditions, typically user input but can also include things like security (ie making sure the user can only see what they're authorized to see), user settings and so on.

The example data model from this series are the beginning of the Party data model, which is very common in CRM and related fields. There are many variations on this model.

Such systems will inevitably have a search screen to find clients, users, employees and the like. Anyone who has done this knows that it can be, depending on your persistence framework, anything from really tedious to incredibly painful. It could involve dynamically building a SQL statement, which can have problems with getting the parameter binding done correctly, or programmatic queries (eg LINQ-to-SQL in the .Net world) to the incredibly painful world of JPA native queries, especially if done dynamically.

Ibatis has a custom solution for this kind of problem that, in my opinion, is the easiest to use, easiest to understand and most powerful of any of these solutions.

Firstly, let's lay the groundwork.

FindParty.java

package com.cforcoding;

public class FindParty {
    private String name;
    private String phone;
    private String email;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getPhone() { return phone; }
    public void setPhone(String phone) { this.phone = phone; }
    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }
}

This class will store the query criteria. In our example, we will be able to search for a party based on their name, a phone number, an email address or a combination of these.

PartyDAOImpl.java

@Override
@SuppressWarnings("unchecked")
@Transactional(propagation = Propagation.SUPPORTS)
public List findParties(FindParty find) {
    return getSqlMapClientTemplate().queryForList("findParties", find);
}

Once again the interface is assumed.

Party.xml

<select id="findParties" parameterClass="com.cforcoding.FindParty" resultMap="party">
    SELECT pt.id, party_type, name, title, given_names, last_name, date_of_birth, company_number, state_of_incorporation, ct.id contact_id, contact_type, details
    FROM party pt
    LEFT OUTER JOIN person ps ON pt.id = ps.id
    LEFT OUTER JOIN company cy ON pt.id = cy.id
    LEFT OUTER JOIN contact ct ON pt.id = ct.party_id

    <dynamic prepend="WHERE">

        <isNotNull property="name" prepend="AND">
            pt.name LIKE #name#
        </isNotNull>

        <isNotNull property="email" prepend="AND">
            EXISTS (SELECT id FROM contact WHERE party_id = pt.id AND contact_type = 'E' AND detailsLIKE #email#)
        </isNotNull>

        <isNotNull property="phone" prepend="AND">
            EXISTS (SELECT id FROM contact WHERE party_id = pt.id AND contact_type IN ('P','W','M') AND details LIKE #email#)
        </isNotNull>

    </dynamic>

</select>

Now this starts to get interesting.

The first thing to notice is that the crux of the query and the result map are the same as our selectAllParties query. We're definitely getting some value out of reuse of the result map.

There are three conditional clauses in this statement, one for each of our properties. Each of these are saying that if the specified property is not null then include the enclosed condition in the query. The enclosed text is nothing more than SQL with Ibatis parameter mappings.

It's worth noting that each conditional clause has an attribute of prepend="AND". This means that if the condition is true (and thus the cluase is included) then prepend the SQL with AND. If you've done dynamically built SQL statements, you'll know that issues such as this can be hugely problematic.

But it gets better. All of these clauses are wrapped in a <dynamic> element. This tag tells Ibatis that the first true condition will have it's prepend replaced by the dynamic prepend.

The result of this is that if all three properties are null, there will be no WHERE clause at all. If you specify any combination of one, two or three of the properties, a correct SQL statement will be generated.

Testing the Code

public static void main(String args[]) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    PartyDAO partyDAO = (PartyDAO) ctx.getBean("partyDAO");
    FindParty find = new FindParty();
    find.setEmail("susan%");
    List parties = partyDAO.findParties(find);
    int i = 0;
    for (Party party : parties) {
        List contacts = party.getContacts();
        System.out.printf("[%d/%d] %s: %s has %d contacts%n", ++i, parties.size(), party.getType().getDescription(), party.getName(), contacts.size());
        int j = 0;
        for (Contact contact : party.getContacts()) {
            System.out.printf("  [%d/%d] %s: %s%n", ++j, contacts.size(), contact.getType().getDescription(), contact.getValue());
        }
    }
}

Try it with any combination of criteria and it will work. This simple example does of course have some serious limitations:

  • It requires Oracle wildcard syntax. You can of course wrap present this to the user anyway you like and transform it before passing it to the query (eg by appending % automatically);
  • Oracle, by default, uses case-sensitive text matching so "SUSAN%" will return nothing in the above code. This typically isn't what user's want but efficient case-insensitive searching is beyond the scope of this post;
  • This example doesn't efficiently find matches within a column that don't start at the beginning of the column due to how indexes work. Again this isn't necessarily what you want or expect as a user but again, efficient full-text searching is beyond the scope of this post.

Conditional Clauses

Ibatis has a wide range of conditions available. From the reference documentation:

Property Description
<isPropertyAvailable> Checks if a property is available (i.e is a property of the parameter bean)
<isNotPropertyAvailable> Checks if a property is unavailable (i.e not a property of the parameter bean)
<isNull> Checks if a property is null.
<isNotNull> Checks if a property is not null.
<IsEmpty> Checks to see if the value of a Collection, String or String.valueOf() property is null or empty ("" or size() < 1).
<isNotEmpty> Checks to see if the value of a Collection, String or String.valueOf() property is not null and not empty ("" or size() < 1).

Example Usage:

<isNotEmpty prepend=”AND” property=”firstName” >
FIRST_NAME=#firstName#
</isNotEmpty>
<isEqual> Checks the equality of a property and a value, or another property.
<isNotEqual> Checks the inequality of a property and a value, or another property.
<isGreaterThan> Checks if a property is greater than a value or another property.
<isGreaterEqual> Checks if a property is greater than or equal to a value or another property.
<isLessThan> Checks if a property is less than a value or another property.
<isLessEqual> Checks if a property is less than or equal to a value or another property.

Example Usage:

<isLessEqual prepend=”AND” property=”age” compareValue=”18”>
ADOLESCENT = ‘TRUE’
</isLessEqual>
<isParameterPresent> Checks to see if the parameter object is not present (null).
<isParameterNotPresent> Checks to see if the parameter object is not present (null).

Example Usage:

<isNotParameterPresent prepend=”AND”>
EMPLOYEE_TYPE = ‘DEFAULT’
</isNotParameterPresent>

Template Reuse

Firstly, it's worth pointing out that if you specify no conditions (by putting null in all the FindParty properties or by passing null as a parameter) that this works exactly like the selectAllParties query we developed previously. As such you could combine them. Let's assume for the purpose of this example that we don't wish to do this.

Ibatis allows us to create reusable SQL fragments in an easy way. Our two queries could be replaced with:

<sql id="selectParties">
    SELECT pt.id, party_type, name, title, given_names, last_name, date_of_birth, company_number, state_of_incorporation, ct.id contact_id, contact_type, details
    FROM party pt
    LEFT OUTER JOIN person ps ON pt.id = ps.id
    LEFT OUTER JOIN company cy ON pt.id = cy.id
    LEFT OUTER JOIN contact ct ON pt.id = ct.party_id
</sql>

<select id="selectAllParties" resultMap="party">
    <include refid="selectParties"/>
</select>

<select id="findParties" parameterClass="com.cforcoding.FindParty" resultMap="party">
    <include refid="selectParties"/>

    <dynamic prepend="WHERE">

        <isNotNull property="name" prepend="AND">
            pt.name LIKE #name#
        </isNotNull>

        <isNotNull property="email" prepend="AND">
            EXISTS (SELECT id FROM contact WHERE party_id = pt.id AND contact_type = 'E' AND details LIKE #email#)
        </isNotNull>

        <isNotNull property="phone" prepend="AND">
            EXISTS (SELECT id FROM contact WHERE party_id = pt.id AND contact_type IN ('P','W','M') AND details LIKE #email#)
        </isNotNull>

    </dynamic>

</select>

It couldn't be easier.

Conclusion

As you can see, I've barely scratched the surface of what's possible. The example used is necessarily simple and it might fool you into thinking it's easy to construct a prepared statement to do the same in, say, Spring JDBC or JPA. While you can do this, it'll be harder to read, more tedious to code and more error-prone. It will also quickly turn into a quagmire of complexity as your conditional logic becomes more complex.

Starting a Programming Blog

It is one month ago today that I decided to start a programming blog. I wasn't sure what to expect but I get questions both here and in the real world so I thought I'd jot down some my experiences.

Why Start a Blog?

Fellow StackOverflow user, Brent Ozar goes into this in a way I see no need to repeat in Sow to Start a Blog. Jeff Atwood probably tipped me over the edge when I read How To Achieve Ultimate Blog Success In One Easy Step. Not from any grandiose notions of reaching those giddy heights. It was more the just do it attitude.

I'll just say that for me it's not about earning money from the blog. Other than Joel Spolsky, Jeff Atwood and one or two dozen others, I don't think anyone makes enough money from blogging to justify the effort (compared to, say, spending that time getting paid for professional services). For me, there were (and are) several reasons:

  1. To have a demonstrable body of work on a variety of topics;
  2. To make me a better communicator;
  3. To raise issues I feel are worth discussing such as the future direction of Java;
  4. To write tutorials on technologies, platforms or libraries that I don't think have been sufficiently covered, like my ongoing series on Ibatis; and
  5. For fun.

Interestingly, many of these reasons are the same as why I chose to become a frequent and regular contributor on StackOverflow. StackOverflow luminary and contributor extraordinaire, Jon Skeet has written about writing helpful technical answers, which is worth reading.

Recently someone suggested that new users are at a disadvantage to experienced users in terms of gaining reputation. In a way they're right but not because of the implied reason that people vote for and accept answers just because an "old-timer" answered it but because--speaking as someone who has answered over 1,000 questions on StackOverflow--answering questions makes you better at answering questions.

Blaise Pascal once said:

The present letter is a very long one, simply because I had no leisure to make it shorter.

This applies very much to answering questions and writing blog posts and doing pretty much any form of written communication: there is a skill and an art to writing something that is long enough to be useful and short enough to be read and succinct. I see the difference in my own answers on StackOverflow now versus 6 months ago.

The other positive aspect of answering questions is that it's a really good way to teach yourself a new skill. I had read large chunks of "jQuery in Action", which is a fine book, but it's only when a library is met by other programmers that you start seeing real world cases and problems of how to apply it. I taught myself jQuery by answering questions on StackOverflow.

What to Write?

You get a lot of people who ask "I'm new to PHP, what framework should I use?" Likewise, I've seen people ask "I want to start a blog, what should I write about?" In both cases, people can miss the point: a framework is a tool. It exists to solve a problem you have rather than being a security blanket. It may sound trite but only start a blog if you have something to say.

Other people write very narrowly focused, highly technical blogs that can be of excellent value. I've always been more of a generalist so I deliberately chose not to restrict myself too narrowly, which is why you see posts here about PHP, Java, CSS, ORMs and SQL and a mix of both technical posts and opinion pieces. Is this a good idea? I have no idea. Like I said, I'm new to this.

What to Expect?

I didn't know what to expect going in to this. Would 10 people a day read my blog? 1000? None? This is the situation after exactly one month, according to Google Analytics:

Are these numbers good? Bad? I honestly don't know. I will say that I am pleased and this has exceeded my most optimistic yet nebulous expectations.

My Advice

It may be premature or even presumptuous to start dispensing advice so soon but I have learnt a thing or two worth conveying:

  1. Start from day one under your own domain name;
  2. Use Blogger. Using your own domain is free. It is a paid feature on Wordpress. You could of course host it yourself but why would you?
  3. Turn off "Convert line breaks" on Blogger. It's a real pain to change later;
  4. You're better off writing HTML than you are using the inbuilt editor;
  5. Find, choose and cuustomize a good template. Obviously this is subjective but you don't want your blog looking like every other blog;
  6. Use Google Analytics
  7. Use Kontactr for a contact form. Make sure you have a contact form;
  8. Link any relevant profiles like LinkedIn or StackOverflow or any open source projects you contribute to;
  9. Add a selection of social news links (eg DZone, Digg, Reddit, Twitter) to your profile;
  10. Put your content on the left. This is easier to read on lower-resolution displays, especially mobile devices;
  11. Use Feedburner for RSS feeds; and
  12. DZone is your friend. Of course this will vary depending on your content. For me it has been the biggest traffic source by far.

So there you have it. I must admit to having a Seinfeld moment writing this post. Much like "the show about the show", this is now "the blog about the blog". I hope these observations and tips are of use to you.

Continued in Part 2.

Ibatis Tutorial: Aggregation with groupBy

This tutorial builds on the knowledge from the Ibatis Inheritance Tutorial. As such the configuration and schema won't be repeated here for brevity.

The next feature I will introduce is dynamic grouping. To explain this let me pose a question: have you ever done an aggregation query (meaning it has a GROUP BY clause) and wanted to return columns other than the ones you're grouping by or aggregating in some way? Well, Ibatis has an answer for you.

Consider the example from the previous tutorial: we are now returning a list of parties that have different types. Those parties have contact information (eg email addresses and various types of phone numbers). What if we wanted to return a list of all our parties as well as their contact information?

In straight SQL there would be a number of options:

  1. Join party to contact and handle the duplicate parties;
  2. Retrieve the contacts individually for each party we process; or
  3. Query the parties and then do a separate query for all the contacts.

All of these options have downsides. The last one results in two queries and is quite efficient but requires record matching in code. It's also fine if you really need all the contact records but what if you're only returning a subset of the parties? In that case you need to query more contacts than you need or join to the party table anyway.

Ibatis has a solution that is very much like (1) from above but Ibatis does most of the legwork for you. Let's start with the Java classes we need.

Contact.java

package com.cforcoding;

public class Contact {
    private Long id;
    private ContactType type;
    private String value;

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public ContactType getType() { return type; }
    public void setType(ContactType type) { this.type = type; }
    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }
}

ContactType.java

package com.cforcoding;

public enum ContactType {
    EMAIL("E", "Email"),
    PHONE("P", "Phone"),
    WORK("W", "Work"),
    MOBILE("M", "Mobile");

    private final String code;
    private final String desc;

    ContactType(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() {
        return code;
    }

    public String getDescription() {
        return desc;
    }

    public static ContactType find(String code) {
        for (ContactType type : values()) {
            if (type.code.equals(code)) {
                return type;
            }
        }
        return null;
    }
}

ContactTypeHandler.java

package com.cforcoding;

import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
import com.ibatis.sqlmap.client.extensions.ParameterSetter;
import com.ibatis.sqlmap.client.extensions.ResultGetter;

import java.sql.SQLException;

public class ContactTypeHandler implements TypeHandlerCallback {
    @Override
    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
        setter.setString(parameter == null ? null : ((ContactType)parameter).getCode());
    }

    @Override
    public Object getResult(ResultGetter getter) throws SQLException {
        return valueOf(getter.getString());
    }

    @Override
    public Object valueOf(String s) {
        return s == null ? null : ContactType.find(s);
    }
}

All the above are a straightforward adaption of the previous tutorial.

Party.java

package com.cforcoding;

import java.util.List;
import java.util.ArrayList;

public class Party {
    private Long id;
    private PartyType type;
    private String name;
    private List contacts = new ArrayList();

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public PartyType getType() { return type; }
    public void setType(PartyType type) { this.type = type; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public List getContacts() { return contacts; }
    public void setContacts(List contacts) { this.contacts = contacts; }
}

The only change here is the addition of a list of contacts. Ibatis will populate that for us.

Party.xml

<resultMap id="contact" class="com.cforcoding.Contact">
    <result property="id" column="contact_id"/>
    <result property="type" column="contact_type" typeHandler="com.cforcoding.ContactTypeHandler"/>
    <result property="value"  column="details"/>
</resultMap>

<resultMap id="party" class="com.cforcoding.Party" groupBy="id">
    <result property="id" column="ID"/>
    <result property="type" column="party_type" typeHandler="com.cforcoding.PartyTypeHandler"/>
    <result property="name" column="NAME"/>
    <result property="contacts" resultMap="Party.contact"/>
    <discriminator javaType="com.cforcoding.PartyType" column="PARTY_TYPE" typeHandler="com.cforcoding.PartyTypeHandler">
        <subMap value="PERSON" resultMap="person"/>
        <subMap value="COMPANY" resultMap="company"/>
    </discriminator>
</resultMap>

<resultMap id="person" class="com.cforcoding.Person" extends="party">
    <result property="title" column="TITLE"/>
    <result property="givenNames" column="GIVEN_NAMES"/>
    <result property="lastName" column="LAST_NAME"/>
    <result property="dateOfBirth" column="DATE_OF_BIRTH"/>
</resultMap>

<resultMap id="company" class="com.cforcoding.Company" extends="party">
    <result property="comapnyNumber" column="COMPANY_NUMBER"/>
    <result property="stateOfIncorporation" column="STATE_OF_INCORPORATION"/>
</resultMap>

<select id="selectAllParties" resultMap="party">
    SELECT pt.id, party_type, name, title, given_names, last_name, date_of_birth, company_number, state_of_incorporation, ct.id contact_id, contact_type, details
    FROM party pt
    LEFT OUTER JOIN person ps ON pt.id = ps.id
    LEFT OUTER JOIN company cy ON pt.id = cy.id
    LEFT OUTER JOIN contact ct ON pt.id = ct.party_id
</select>

This has a couple of new features.

The first is the groupBy attribute on line 7. You can specify one or more property names here. For every distinct set of values for the grouping criteria, a row will be in the result list. Here we're grouping by party id so we will get one row per party.

Internally Ibatis is loading all the rows and effectively sorting them. I believe it may be actually bucketing them in a map of some sort but that is merely an implementation detail. All you need to know is that sort order does not matter for the purpose of grouping.

This is both a good and bad feature. I personally feel that sorting can be done efficiently and effectively in the database, which would still allow effective paging. This also negatively impacts row handlers (I subject I shall soon address) to the point where the Ibatis powers-that-be have decreed row handlers and groupBy to be incompatible.

I raised a JIRA issue to create at least a non-sorting version of this but there has been no action to date.

Line 11 demonstrates the other part of this behaviour. The contacts property is a list. With this result element we are saying that we should fill the contacts list with objects and map them using the Party.contact result map. Note: The namespace needs to be specified for this to work even though namespaces (an optional feature) are disabled. I consider this a bug.

Running the Example

Now to prove this SQL works, we should have a party with no contacts. Run this:

INSERT INTO party VALUES (party_seq.nextval, 'C', 'Kaos Inc');
INSERT INTO company VALUES (party_seq.currval, '111 222 333', 'SA');

Run the query to verify the results.

Now run this sample program:

public static void main(String args[]) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    PartyDAO partyDAO = (PartyDAO) ctx.getBean("partyDAO");
    List parties = partyDAO.selectAllParties();
    int i = 0;
    for (Party party : parties) {
        List contacts = party.getContacts();
        System.out.printf("[%d/%d] %s: %s has %d contacts%n", ++i, parties.size(), party.getType().getDescription(), party.getName(), contacts.size());
        int j = 0;
        for (Contact contact : party.getContacts()) {
            System.out.printf("  [%d/%d] %s: %s%n", ++j, contacts.size(), contact.getType().getDescription(), contact.getValue());
        }
    }
}

This will output the following results:

[1/4] Person: Mr Tom Smith has 3 contacts
  [1/3] Email: tom.smith@example.com
  [2/3] Phone: 919-223-1234
  [3/3] Mobile: 919-223-1235
[2/4] Person: Test has 3 contacts
  [1/3] Email: susan.jones@example.com
  [2/3] Mobile: 919-223-1237
  [3/3] Phone: 919-223-1236
[3/4] Company: Acme Inc has 2 contacts
  [1/2] Email: roger@acme.com
  [2/2] Work: 919-223-1238
[4/4] Company: Kaos Inc has 0 contacts

Consider for a moment just how much work is being done with these few lines of XML.

Conclusion

Dynamic grouping is an incredibly powerful and easy-to-use feature. It is worth noting that groupings can be nested. That means you could easily create a query that constructs a list, one row per party type, where each element has a list of parties of that type and each party has a list of contacts.

JPA would handle the above situation with eager or lazy fetching, as desired. The batch mode in EclipseLink handles this situation very well. Still the point here is to demonstrate that you can achieve extremely sophisticated functionality with just SQL and a few lines of XML.

Soon I shall explore an alternative strategy using lazy fetching.

Next: Ibatis Tutorial: Dynamic SQL

Ibatis Tutorial: Inheritance Strategies

The first advanced feature of Ibatis I want to introduce is inheritance strategies. Several concepts and features will be introduced along the way. This tutorial assumes the knowledge and code from my Spring and Ibatis Tutorial.

Imagine we wanted to select all the parties in our database.

Party.java

package com.cforcoding;

public class Party {
    private Long id;
    private String type;
    private String name;

    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getType() { return type; }
    public void setType(String type) { this.type = type; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

Party.xml

<select id="selectAllParties" resultClass="com.cforcoding.Party">
  SELECT id, party_type type, name
  FROM party
</select>

PartyDAOImpl.java

    @Override
    @SuppressWarnings("unchecked")
    @Transactional(propagation = Propagation.SUPPORTS)
    public List<Party> selectAllParties() {
        return getSqlMapClientTemplate().queryForList("selectAllParties");
    }

The obvious interface is omitted and will be simply assumed in future. The one addition to this code that's worth noting is the addition of the @Transactional annotation declaring this method supports transactions. Why? It's a select so doesn't require a transaction but if it were to run within a transaction, you would probably want it to select data not yet committed by that transaction.

This code uses the queryForList() API call. This simply returns all the rows in a List. I've given that list a generic type, which is the reason for @SuppressWarnings("unchecked"). Java can't know the type of objects returned.

This simple code works fine but can be improved. The first thing to note is that Party has a type code of P or C, stored as a string. This is an obvious candidate for being an enumeration.

PartyType.java

package com.cforcoding;

public enum PartyType {
    PERSON("P", "Person"),
    COMPANY("C", "Company");
    
    private final String code;
    private final String desc;

    PartyType(String code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public String getCode() {
        return code;
    }
    
    public String getDescription() {
        return desc;
    }
    
    public static PartyType find(String code) {
        for (PartyType type : values()) {
            if (type.code.equals(code)) {
                return type;
            }
        }
        return null;
    }
}

This is a basic enum with a couple of features added. Firstly, it has state, being the code and a short description of what that code means. Secondly, there is a finder method to map a code as text to a particular instance.

Ibatis supports the mapping of custom types to and from the database through type handlers.

PartyTypeHandler.java

A type handler can implement either TypeHandlerCallback or TypeHandler. I generally favour the former for having a simpler interface.

package com.cforcoding;

import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;
import com.ibatis.sqlmap.client.extensions.ParameterSetter;
import com.ibatis.sqlmap.client.extensions.ResultGetter;

import java.sql.SQLException;

public class PartyTypeHandler implements TypeHandlerCallback {
    @Override
    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
        setter.setString(parameter == null ? null : ((PartyType)parameter).getCode());
    }

    @Override
    public Object getResult(ResultGetter getter) throws SQLException {
        return valueOf(getter.getString());
    }

    @Override
    public Object valueOf(String s) {
        return s == null ? null : PartyType.find(s);
    }
}

There are two ways that a type handler can be used: locally or globally.

A global type handler is placed in the Ibatis config file. Currently this is sqlmap-config.xml. It would change the file to this:

<sqlMapConfig>
    <typeHandler javaType="com.cforcoding.PartyType" callback="com.cforcoding.PartyTypeHandler"/>
    <sqlMap resource="com/cforcoding/Party.xml"/>
</sqlMapConfig>

Alternatively, it can be used locally, meaning within an explicit result map or parameter map or within an implicit parameter map. We have not used explicit result mapping yet. It looks like this:

<resultMap id="party" class="com.cforcoding.Party">
    <result property="id" column="ID"/>
    <result property="type" column="party_type" typeHandler="com.cforcoding.PartyTypeHandler"/>
    <result property="name" column="NAME"/>
</resultMap>

<select id="selectAllParties" resultMap="party">
    SELECT id, party_type, name
    FROM party
</select>

Explicit result maps allow you more control but are also more verbose. In the interests of brevity, they should generally only be used when you need to use them.

OK, so where's the inheritance?

In the first tutorial, we developed a simple query for selecting a specific person. In this one, we have a query for returning all parties. But let's say we have returned a list of parties and know whether they are people or companies. With only the party object we would need to do a query for each party record to get additional Person or Company details (or use some convoluted IN clause). We have a simple object hierarchy:

  • Party
  • Person extends Party
  • Company extends Party

As it turns out, we need explicit result maps to implement this inheritance structure.

Person.java

package com.cforcoding;

import java.sql.Date;

public class Person extends Party {
    private String title;
    private String givenNames;
    private String lastName;
    private Date dateOfBirth;

    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getGivenNames() { return givenNames; }
    public void setGivenNames(String givenNames) { this.givenNames = givenNames; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public Date getDateOfBirth() { return dateOfBirth; }
    public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; }
}
Note: Now Person extends the Party class.

Company.java

package com.cforcoding;

public class Company extends Party {
    private String comapnyNumber;
    private String stateOfIncorporation;

    public String getComapnyNumber() { return comapnyNumber; }
    public void setComapnyNumber(String comapnyNumber) { this.comapnyNumber = comapnyNumber; }
    public String getStateOfIncorporation() { return stateOfIncorporation; }
    public void setStateOfIncorporation(String stateOfIncorporation) { this.stateOfIncorporation = stateOfIncorporation; }
}

Party.xml

<resultMap id="party" class="com.cforcoding.Party">
    <result property="id" column="ID"/>
    <result property="type" column="party_type" typeHandler="com.cforcoding.PartyTypeHandler"/>
    <result property="name" column="NAME"/>
    <discriminator javaType="com.cforcoding.PartyType" column="PARTY_TYPE" typeHandler="com.cforcoding.PartyTypeHandler">
        <subMap value="PERSON" resultMap="person"/>
        <subMap value="COMPANY" resultMap="company"/>
    </discriminator>
</resultMap>

<resultMap id="person" class="com.cforcoding.Person" extends="party">
    <result property="title" column="TITLE"/>
    <result property="givenNames" column="GIVEN_NAMES"/>
    <result property="lastName" column="LAST_NAME"/>
    <result property="dateOfBirth" column="DATE_OF_BIRTH"/>
</resultMap>

<resultMap id="company" class="com.cforcoding.Company" extends="party">
    <result property="comapnyNumber" column="COMPANY_NUMBER"/>
    <result property="stateOfIncorporation" column="STATE_OF_INCORPORATION"/>
</resultMap>

<select id="selectAllParties" resultMap="party">
    SELECT pt.id, party_type, name, title, given_names, last_name, date_of_birth, company_number, state_of_incorporation
    FROM party pt
    LEFT OUTER JOIN person ps ON pt.id = ps.id
    LEFT OUTER JOIN company c ON pt.id = c.id
</select>

This is where it gets interesting. There are a number of new features here:

  1. Result maps can extend other result maps. Note: No inheritance is implied by result map extension. It is probably more accurate to describe it as inclusion rather than extension as it simply avoids having to copy-and-paste the result definitions;
  2. There is now a <discriminator> element in the party result map. The discriminator column declares which column will determine what type each row from the result set will be. The values are enumerated as <subMap> child elements;
  3. The sub-maps extend the party result map but this is not required. It is however--in this case at least--a convenience. Remember result map extension is not quite the same thing as object inheritance even though the two will tend to go hand in hand;
  4. A type handler is used on the discriminator so the values can be enum values. An equally valid strategy would've been to leave them as strings and use the codes P and C. You can use whichever approach you prefer.

And that's it. It's as straightforward as that. The unfortunate part of this is that the above behaviour isn't documented in the Ibatis reference PDF.

Compared to JPA

Those of you familiar with JPA will find the above familiar. Party is a mapped superclass. The behaviour models the joined table inheritance strategy. In JPA parlance, party type is also a discriminator column.

JPA providers vary in how they handled the joined table scenario from a SQL point of view. Depending on the provider and the number of subclasses the provider will either use a left outer join as we have done. Alternatively, the provider may opt to get all the parties in one query and, depending on query hints and the like, may get all the persons in the second query and all the companies in the third. This is certainly how EclipseLink operates in batch mode, which can be a powerful alternative, particularly if the number of subclasses is large.

Interestingly, Ibatis is more flexible than standard JPA with enum handling. Standard JPA does not have a means of doing custom type conversion. Standard JPA has only two ways of treating enums: by their name() or by their ordinal(). This is one part of JPA I find highly unsatisfactory as using one-character codes is a common idiom in databases. While you can use one letter names for your enum values, this does not lend itself to readable code, particularly if combined with static imports.

Individual providers may have proprietary extensions to go beyond standard JPA, such as custom type mapping or overriding the SQL dialect.

JPA's inheritance strategies also apply to database writes whereas that behaviour isn't quite as straightforward in Ibatis. Updates and inserts are probably best handled via the somewhat ugly use of instanceof.

Conclusion

Inheritance mapping is a powerful and useful feature of any persistence framework. It's not well-known--and certainly not well-documented--that Ibatis has this feature. It's features like this that, for me, enable Ibatis to pull ahead of other lower-level frameworks like Spring JDBC.

I believe that Ibatis really is on the "sweet spot" of complexity vs capability for persistence frameworks, offering most of the (useful) features of JPA with significantly less complexity. This tutorial is another in the series that I hope will demonstrate that.

Update on the G1 Garbage Collector Controversy

Several days ago in The Monetization of Java Begins? I spoke about news reported by Slashdot that the much-anticipated G1 garbage collector had become available in Java 6 Update 14 but appeared to be a commercial feature.

Sun has now clarified their position:

The Janitor is told that an earlier version of the release notes implied that you had to have a support contract to use G1. Some certainly ran with that ball and even made a topical story out of it !


Our bad. So we made the notes clearer, because you don't need to do anything special to try it out. Unless you want your kinks fixed before everyone else gets the fixes either in the next update or in JDK 7. In which case, there is nice little program waiting just for you :)

I hope that puts the matter to rest. Still, I do have to wonder. There is a wide gulf between "permitted" and "not recommended", particularly in reference to the "requirement" of a Java support contract. Was this an about-face by Sun? Who knows? Just call me a happy camper that Sun isn't committing suicide (moreso).

Spring and Ibatis Tutorial

Continuing my series on series on object-relational mapping, Last week, I promised to discuss Ibatis. This is the first in a series of posts I'm planning to write on Ibatis and several of its poorly-documented advanced features.

Because of the various steps involved and my desire to keep these posts readably short, this post will do little more than explain the main features of Ibatis and then give you the sample data and code to connect to Ibatis and do some simple queries. Future posts will detail more advanced features once the foundation is set.

Ibatis is a library for retrieving and persisting objects to and from a database. Firstly, there are a few things it isn't or doesn't do:

  • It isn't an ORM in the strictest sense. It uses SQL. To me, a key requirement of being a "true" ORM is that a true ORM will generate the SQL for you thus (hopefully) providing some database vendor independence;
  • It does not implicitly persist objects like JPA does; and
  • It doesn't require any advanced Java features such as load-time weaving;

Now what it is and what it does:

  • It is extremely lightweight;
  • It can be viewed as a JDBC framework rather than an ORM;
  • It has what is probably the best dynamic SQL framework I've seen;
  • Easy to learn for anyone who has learnt SQL because it is SQL. There is no proprietary query language to learn;
  • It is a POJO persistence framework in the purest sense: there are no annotations; and
  • It stores queries in external XML files. Some will view this as a disadvantage. I do not. External XML files are easy to search (and filter by file type), easy to read, the queries aren't buried in code with a billion string concatenations (like Spring JDBC can end up) and it's relatively easy to cut and paste queries into a SQL tool to test them, typically much easier than SQL embedded in code is.

If you look around the Web you'll see some more claims made about Ibatis:

  • It has no object cache;
  • It does not do lazy loading of related entities; and
  • It does not handled mapped superclasses/discriminated types.

Each of the above claims is wrong. But more on that in later posts. First, the basics. You will need:

Note: The Spring with dependencies download includes all the above required libraries (and a whole lot more) with the versions validated for that particular Spring release.

Spring isn't strictly required for this and the examples will be in a J2SE environment but with Spring the examples can easily be dropped into a typical Web application environment where Spring is near-ubiquitous.

As an alternative, all of the above can be resolved with Maven, which is left as an exercise for the reader.

Create a user/schema in the Oracle admin tool (which is just a Web page). Make sure you give it the CONNECT and RESOURCE roles and the CREATE TABLE and CREATE SEQUENCE privileges.

I use the following conventions with Oracle:

  • Keywords are always in UPPERCASE;
  • Database object names are always in lowercase;
  • No spaces will be put in database object names;
  • Underscores will replace spaces (ie there won't be any camel case conventions that are common on, say, SQL Server);
  • Primary keys will pretty much always be named 'id';
  • Referential integrity will be enforced;
  • Integer values (including table ids) will generally always be NUMBER(19,0). The reason for this is that this will fit in a 64-bit signed integer thus allowing the Java long type to be used instead of the more awkward BigInteger;
  • Despite the misnomer of appending "_number" to some column names, the type of such columns will be VARCHAR2 not a number type. Number types are reserved for primary keys and columns you do arithmetic on;
  • I always use a technical primary keys (Item 3); and
  • Each table will have its own sequence for key generation. The name of that sequence will be <table_name>_seq.

The above is stated purely so you can more easily follow what I'm doing and I would certainly recommend such practices but opinions will vary. Use whatever standard you're comfortable with it. Just be consistent.

Schema and Sample Data

CREATE TABLE party (
  id NUMBER (19,0) PRIMARY KEY,
  party_type CHAR(1) NOT NULL,
  name VARCHAR2(200)
);

CREATE TABLE person (
  id NUMBER (19,0) PRIMARY KEY,
  title VARCHAR2(30),
  given_names VARCHAR2(80),
  last_name VARCHAR2(80),
  date_of_birth TIMESTAMP,
  FOREIGN KEY (id) REFERENCES party (id)
);

CREATE TABLE company (
  id NUMBER (19,0) PRIMARY KEY,
  company_number VARCHAR2(20),
  state_of_incorporation VARCHAR2(20),
  FOREIGN KEY (id) REFERENCES party (id)
);

CREATE TABLE contact (
  id NUMBER(19,0) PRIMARY KEY,
  party_id NUMBER(19,0) NOT NULL,
  contact_type CHAR(1) NOT NULL,
  details VARCHAR2(100) NOT NULL,
  FOREIGN KEY (party_id) REFERENCES party (id)
);

CREATE SEQUENCE party_seq;
CREATE SEQUENCE contact_seq;

INSERT INTO party VALUES (party_seq.NEXTVAL, 'P', 'Mr Tom Smith');
INSERT INTO person VALUES (party_seq.CURRVAL, 'Mr', 'Tom', 'Smith', '11-JUL-1970');
INSERT INTO contact VALUES (contact_seq.NEXTVAL, party_seq.CURRVAL, 'E', 'tom.smith@example.com');
INSERT INTO contact VALUES (contact_seq.NEXTVAL, party_seq.CURRVAL, 'P', '919-223-1234');
INSERT INTO contact VALUES (contact_seq.NEXTVAL, party_seq.CURRVAL, 'M', '919-223-1235');
INSERT INTO party VALUES (party_seq.NEXTVAL, 'P', 'Ms Susan Jones');
INSERT INTO person VALUES (party_seq.CURRVAL, 'Ms', 'Susan', 'Jones', '27-NOV-1955');
INSERT INTO contact VALUES (contact_seq.NEXTVAL, party_seq.CURRVAL, 'E', 'susan.jones@example.com');
INSERT INTO contact VALUES (contact_seq.NEXTVAL, party_seq.CURRVAL, 'P', '919-223-1236');
INSERT INTO contact VALUES (contact_seq.NEXTVAL, party_seq.CURRVAL, 'M', '919-223-1237');
INSERT INTO party VALUES (party_seq.NEXTVAL, 'C', 'Acme Inc');
INSERT INTO company VALUES (party_seq.CURRVAL, '123 456 789', 'WA');
INSERT INTO contact VALUES (contact_seq.NEXTVAL, party_seq.CURRVAL, 'E', 'roger@acme.com');
INSERT INTO contact VALUES (contact_seq.NEXTVAL, party_seq.CURRVAL, 'W', '919-223-1238');

database.properties

It's typical (and good practice) to externalize things like connection details as they often vary between environments (eg development, test, production).

database.username=scratch
database.password=scratch
database.class=oracle.jdbc.OracleDriver
database.url=jdbc:oracle:thin:@localhost:1521:XE

sqlmap-config.xml

Top-level Ibatis configuration. Not much needed here because Spring will do most of it. For now we're just loading one set of queries in Party.xml.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
        PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
        "http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
    <sqlMap resource="com/cforcoding/Party.xml"/>
</sqlMapConfig>

Party.xml

This contains some queries that we're doing on Party entities. The organization and naming of queries is entirely arbitrary. I suggest you pick a convention and stick with it. I put things related to an entity in a namespace and an XML file with that entity's name. Typically that file is in the same directory as the DAO that uses it.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
        PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
        "http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="Party">

    <select id="selectPerson" parameterClass="long" resultClass="com.cforcoding.Person">
        SELECT  ps.id,
                pt.party_type type,
                pt.name,
                ps.title,
                ps.given_names givenNames,
                ps.last_name lastName,
                ps.date_of_birth dateOfBirth
        FROM person ps
        JOIN party pt ON ps.id = pt.id
        WHERE ps.id = #value#
    </select>

    <update id="updateParty" parameterClass="com.cforcoding.Person">
        UPDATE party
        SET name = #name#
        WHERE id = #id#
    </update>

    <update id="updatePerson" parameterClass="com.cforcoding.Person">
        UPDATE person
        SET title = #title#,
            given_names = #givenNames#,
            last_name = #lastName#,
            date_of_birth = #dateOfBirth:DATE#
        WHERE id = #id#
    </update>

</sqlMap>

There is one select and two updates in this file. You will note that apart from parameters, this is vanilla SQL.

Select Person

This query takes a long (being a person's id) and returns a com.cforcoding.Person object. When the parameter is a single value that can be passed to JDBC directly (eg a primitive, BigDecimal, BigInteger, String, Date) it is referred to with the special name 'value', hence #value# at the end of the select. Lastly, some of the columns selected are aliased to names that match the properties of the Person object (ignoring case). If you do this, Ibatis will automatically map them for you. This is called implicit result mapping.

There are a lot of things you can do with implicit result maps including:

  • Specifying the JDBC type;
  • Specifying the Java type. In this case we're using a concrete value object of known type but that isn't necessarily the case. We could've used a HashMap for the result instead, which obviously would not have automatic Java type discovery. Ibatis will fallback to default behaviour. This allows us to change it;
  • Controlling how NULLs are handled; and
  • Custom type conversion (eg for enums).

This can be taken a step further where explicit result maps are used. These are more verbose but give many more options. More on this in later posts.

Update Party

An extremely simple update statement. Rather than #value#, here you see #name# and #id#. The names within the hashes refer to the object's properties. This is implicit parameter mapping. As with result mapping, you can be more explicit and control things to a greater degree but it's optional.

Update Person

This update is slightly more complex but only adds one thing not yet seen: #dateOfBirth:DATE#. Here I'm being explicit about what JDBC type to use for the conversion from the Java type. This is because we're only interested in the day, month and year, not the hours, minutes and seconds.

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:database.properties"/>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${database.class}"/>
        <property name="url" value="${database.url}"/>
        <property name="username" value="${database.username}"/>
        <property name="password" value="${database.password}"/>
    </bean>
                                         
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <tx:annotation-driven/>

    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation" value="sqlmap-config.xml"/>
        <property name="dataSource" ref="dataSource"/>
        <property name="useTransactionAwareDataSource" value="true"/>
    </bean>

    <bean id="partyDAO" class="com.cforcoding.PartyDAOImpl">
        <property name="sqlMapClient" ref="sqlMapClient"/>
    </bean>

</beans>

What have we configured?

  • A PropertyPlaceholderConfigurer that will dynamically replace ${...} expressions in applicationContext.xml with the values from (in this case) database.properties.
  • A DBCP BasicDataSource;
  • A TransactionManager for Spring transactions (rather than, say, Ibatis transactions);
  • We will use Spring's annotation-driven declarative transactions. The next bean enables automatic consumption of @Transactional annotations (more on this later);
  • A factory for producing SqlMapClients; and
  • The PartyDAO for executing the queries and updates.

Person.java

A value object for the Person entity.

package com.cforcoding;

import java.sql.Date;

public class Person {
    private Long id;
    private String type;
    private String name;
    private String title;
    private String givenNames;
    private String lastName;
    private Date dateOfBirth;

    public Long getId() { return id; } 
    public void setId(Long id) { this.id = id; }
    public String getType() { return type; }
    public void setType(String type) { this.type = type; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }
    public String getGivenNames() { return givenNames; }
    public void setGivenNames(String givenNames) { this.givenNames = givenNames; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    public Date getDateOfBirth() { return dateOfBirth; }
    public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; }
    // equals(), hashCode() and toString() omitted for brevity
}

PartyDAO.java

package com.cforcoding;

public interface PartyDAO {
    Person selectPerson(long id);
    void updatePerson(Person person);
}

PartyDAOImpl.java

package com.cforcoding;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;
import org.springframework.transaction.annotation.Transactional;

public class PartyDAOImpl extends SqlMapClientDaoSupport implements PartyDAO {
    public Person selectPerson(long id) {
        return (Person)getSqlMapClientTemplate().queryForObject("selectPerson", id);
    }

    @Transactional
    public void updatePerson(Person person) {
        getSqlMapClientTemplate().update("updateParty", person, 1);
        getSqlMapClientTemplate().update("updatePerson", person, 1);
        System.out.println("Person updated");
    }
}

All Ibatis operationis are done through a SqlMapClient. PartyDAOImpl extends a Spring class (SqlMapClientDaoSupport) that provides helper functions, one of which is getSqlMapClientTemplate(). This replaces the standard Ibatis exceptions (being Java's checked SQLExceptions) with Spring's DataAccessExceptions (which are runtime exceptions).

I am a firm believer that checked exceptions are a failed experiment but that's another debate.

queryForObject() is passed the name of the query to run and a parameter (being the id). It returns an object that is cast to a Person. As simple as that.

updatePerson() has several features on it. One is the @Transactional annotation. This tells Spring that a transaction is required and to start one if it's not already executing within a transactional context (because the default propagattion is REQUIRED). That method body will execute as one transactional unit and will auto-commit if it compltes successfully. If a runtime exception is thrown, the transaction will be rolled back.

The update() method is very similar to queryForObject() where the first two parameters are the name of the query to run and the parameter to pass. A third optional argument has been specified. This tells Ibatis the number of rows affected should be 1. If the number of rows affected is 0 or more than 1, an exception will be thrown.

Main.java

package com.cforcoding;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String args[]) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        PartyDAO partyDAO = (PartyDAO) ctx.getBean("partyDAO");
        Person person = partyDAO.selectPerson(2);
        person.setName("Test");
        person.setGivenNames("Blah");
        partyDAO.updatePerson(person);
        System.out.println(person);
    }
}

This simple program:

  1. Manually creates a Spring application context by loading applicationContext.xml from the classpath;
  2. Gets the PartyDAO bean from that application context;
  3. Queries a person; and
  4. Makes a simple change to that person, persisting that to the database.

Conclusion

The above is a barebones Spring and Ibatis configuration that can easily be used inside or outside of an application server (or Web container such as Tomcat). The only change necessary for running inside a J(2)EE environment is that a JNDI data source will likely be used instead of a DBCP BasicDataSource but refer to your container documentation for specifics on data access.

The above configuration will be used in upcoming posts on more advanced topics of Ibatis but I hope the above is useful to you. It can be difficult to initially configure Spring if you're unfamiliar with one or more of the underlying frameworks.

Next: Ibatis Inheritance Strategies