<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Technical Notes &#187; hibernate</title>
	<atom:link href="http://blog.anthonychaves.net/category/hibernate/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.anthonychaves.net</link>
	<description>Life is software and jujitsu</description>
	<lastBuildDate>Tue, 16 Feb 2010 22:15:16 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Dummy Code (Quick&#8217;n&#039;Dirty vs. Engineered)</title>
		<link>http://blog.anthonychaves.net/2009/02/04/dummy-code-quickndirty-vs-engineered/</link>
		<comments>http://blog.anthonychaves.net/2009/02/04/dummy-code-quickndirty-vs-engineered/#comments</comments>
		<pubDate>Wed, 04 Feb 2009 22:34:13 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[career]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[work]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/?p=125</guid>
		<description><![CDATA[When creating software, two people will never write the same implementation of a method or system of non-trivial design.  Creating software is a problem solving process and there are usually many ways to solve one problem.  The solutions may differ in elegance and efficiency while giving the same output for a given set of inputs.  [...]]]></description>
			<content:encoded><![CDATA[<p>When creating software, two people will never write the same implementation of a method or system of non-trivial design.  Creating software is a problem solving process and there are usually many ways to solve one problem.  The solutions may differ in elegance and efficiency while giving the same output for a given set of inputs.  A correct solutions is a correct solution regardless of implementation.<span id="more-125"></span></p>
<p>Building software modules within a team requires communicating, at a minimum,  method or class signatures and a general idea of the functionality.  This may include a detailed design document clearly specifying method signature and a pseudo-code implementation or method stubs and comments about what the method should do right in the source file.  One thing that should never be used for communicating a signature and functionality is dummy code.</p>
<p>Dummy code looks like real code.  It is real code but it rarely works.  Even if it compiles it doesn&#8217;t produce the correct result when run.  Nor are there any tests that prove it (in)correct.  Dummy code exists only for the sake of its author&#8217;s gratification.</p>
<p>Rather than talk about the desired functionality through some more productive means the dummy code author thinks he is helpfully providing a guesstimate of correct implementation.  What he has actually done is provide a jumbled mess of non-working code cleverly disguised as a working part of the project when he checks it in to the SCM.  The dummy code author thinks someone else will come along and provide the correct implementation &#8220;later&#8221;.</p>
<p>What he doesn&#8217;t realize is that his dummy code is almost indistinguishable from code that should be part of the project.  The next person to try to reuse this code is in for hours of WTF moments trying to figure out WTF the code is supposed to do and why it&#8217;s got code branching five levels deep.</p>
<p>At my first post-college job I had a wonderful mentor.  Thys (pronounced <em>Tays</em>, like &#8220;taste&#8221; without the second &#8216;t&#8217;) and I were talking one day when he asked me a question.  &#8220;Anthony, if you were given a task of writing software that would only be run once would you do it quick-and-dirty or with a well-measured, engineered approach?&#8221;</p>
<p>How bad could &#8220;quick-and-diry&#8221; be if the software would run only once?  I thought about it for a moment before answering.  Quick and dirty.</p>
<p>Thys said he would never write quick and dirty software.  Doing so figuratively creates a  monkey on the developerment team&#8217;s back.  The software that only needs to run once inevitably needs to run a second time and a third time and eventually run regularly.  Thys helped me realize that software evolves and escapes.  Useful software will run indefinitely.  Any time and effort saved on the initial write will be lost 10 times (I made that number up.  It is actually higher.  Any want to provide real data or examples?) over on the maintenance, bug fixes and rewrites.</p>
<p>Writing dummy code is writing software quick and dirty without any idea of the implementation details.  Bad code and bad algorithms.  The worst of both worlds.  It&#8217;s been a long time since Thys and I had that conversation but the years have proven him right again and again.  Any code worth checking in to SCM is worth writing well.  That includes unit tests.</p>
<p>Dummy code has no place in SCM.  If you&#8217;re not going to correctly implement a method you stub out then don&#8217;t provide an implementation at all.  Leave some comments around the method stub if you have to.  Open a ticket in the issue tracking system to let someone know the method needs an implementation and describe the input and output.  Let the implementer determine which algorithms and data structures to use.</p>
<p>Dummy code is a hazard to software projects.  It violates the principle of least astonishment.  Next time you think about writing some remember that the next person that uses the code will waste time figuring out why it doesn&#8217;t work as expected.</p>
<p>If you check code in to your SCM where other people can pull it, take a look at what you&#8217;re checking in.  Does it work?  Does it work well?  Are there tests that prove it?  Helping someone by coding less is sometimes the best help of all.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/2009/02/04/dummy-code-quickndirty-vs-engineered/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Industry Ignorance</title>
		<link>http://blog.anthonychaves.net/2009/01/22/industry-ignorance/</link>
		<comments>http://blog.anthonychaves.net/2009/01/22/industry-ignorance/#comments</comments>
		<pubDate>Thu, 22 Jan 2009 22:27:41 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[scalability]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/?p=106</guid>
		<description><![CDATA[Today I let out a dejected sigh after reading a response to my latest post on the Joel on Software message board.  My post was just a reminder about the BostonScalability User Group meeting that takes place next Wednesday the 28th.  I post the meeting announcements on JoS and a good number of people hear [...]]]></description>
			<content:encoded><![CDATA[<p>Today I let out a dejected sigh after reading a response to my latest post on the Joel on Software message board.  My post was just a reminder about the BostonScalability User Group meeting that takes place next Wednesday the 28th.  I post the meeting announcements on JoS and a good number of people hear about the meetings from the JoS board.  Every month a few new people tell me they found the group through JoS.  So why did I sigh after reading the response to the meeting reminder?</p>
<p><span id="more-106"></span>I don&#8217;t make any money running the group.  In fact it costs me money.  I do a good amount of planning, coordinating, buying, booking and raffle prize finding for the group.  I love seeing 30+ people show up for our after work meetings.   It&#8217;s a lot of work but I do it because I am passionate about emerging trends in application scalability.  It&#8217;s a labor of love that I find professionally and personally rewarding.  Like all good geeks I maintain a healthy hobby writing software outside business hours.</p>
<p>I&#8217;m interested in building bigger, better software.  Batch applications, database interactions, web applications, CPU intensive algorithims should be as effective as possible.  Effective does not necessarily mean efficient.  Effectiveness is based on price, time-to-market, performance, maintenance cost, etc.  Analyzing these facets is important. The tools used to arrive at the solution are important, too.</p>
<p>BostonSUG has wonderful speakers who generously donate their time to speak at our meetings.  These people come in from all over the country.  Just a few examples, we&#8217;ve hosted Orion Letizi and Nikita Ivanov from San Francisco, Billy Newport from Minnesota and Mike Culver from Washington.  We&#8217;ve had numerous local speakers who we appreciate just as much as those that fly to see us.  Patrick Peralta, Tom O&#8217;Hare and Rakesh Chaudhary had large audiences.  Each of these speakers is an expert in a different facet of building scalable applications.  They are industry leaders in compute clouds, data grids, object caching, streaming media and more.</p>
<p>One thing we haven&#8217;t touched yet is building a web site that performs well.  The person that replied to the meeting reminder said:</p>
<blockquote><p>save your time:<br />
only put strings in session; and only put one item per-user in the session; unless they are doing some heavy form processing.<br />
use native clustering.<br />
put professional load balancer in from.<br />
max pipe.</p></blockquote>
<p>Thanks.  That is a helpful reply.  It immediately invalidates the hours of insightful discussion we&#8217;ve had at past BostonSUG meetings that have spent less than five minutes total dedicated to the problem to which he so generously offers his solution&lt;/sarcasm&gt;.</p>
<p><img class="alignnone size-medium wp-image-107" title="traditional-web-architecture" src="http://blog.anthonychaves.net/wp-content/uploads/2009/01/traditional-web-architecture-300x225.png" alt="traditional-web-architecture" width="300" height="225" /></p>
<p>This is a picture of what is described in the reply post.  As an industry we have known this works for a long time.  That&#8217;s why we don&#8217;t talk about the architecture depicted by this diagram at BostonSUG.  Sure, we&#8217;ll talk about HOW an app server achieve scalability.  We&#8217;ll talk about the implementation, not just about using JBoss or Mongrel.  We got a good look inside GridGain.  We had a guided tour around Terracotta.  BostonSUG evokes deeper discussion than &#8220;small sessions, load balancer, database cluster, done!&#8221;.  <em>You should do that anyway</em>.</p>
<p>Past meetings aside we have a lot to talk about this year.  Rails 3, Java 7, CloudFront, Hibernate Shards, Google AppEngine, tons of topics that push the industry foward.  If you&#8217;re interested in evolving and not building the same old software then come to BostonSUG.  If you&#8217;re interested in building what we know works (and have known since pre-2000) then it&#8217;s not the meeting for you.</p>
<p>I didn&#8217;t intend for this to turn into a meeting plug but I might as well include the link to the meeting announcement.  <a href="http://www.bostonsug.org/2009/01/05/january-28-2009-meeting-announcement/">Wednesday January 28, 2009 @ 6 p.m.  IBM Innovation Center Waltham, MA 02451</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/2009/01/22/industry-ignorance/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Terracotta Tech Talk</title>
		<link>http://blog.anthonychaves.net/2008/05/20/53/</link>
		<comments>http://blog.anthonychaves.net/2008/05/20/53/#comments</comments>
		<pubDate>Tue, 20 May 2008 17:21:01 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[terracotta]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/java/2008/05/20/53</guid>
		<description><![CDATA[I&#8217;m developing a Java/Spring web application deployed on JBoss 4.2.2 that makes heavy use of memcached in order to reduce the latency experienced by the user.  The user interacts with data that is (unbeknownst to them) in the cache.  In order to make changes permanent a message is placed on a queue that [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m developing a Java/Spring web application deployed on JBoss 4.2.2 that makes heavy use of memcached in order to reduce the latency experienced by the user.  The user interacts with data that is (unbeknownst to them) in the cache.  In order to make changes permanent a message is placed on a queue that is picked up by an MDB.  The message is just the key used to access the data in the cache.  The MDB takes the data out of the cache and then saves it to the database.  This greatly improves the user experience because they do not have to wait for the next page to load while costly database writes occur.  So far, so good.</p>
<p>In this case I am unfortunately duplicating data by separating the reads from the writes.  The data lives in the Hibernate session used by the read-only app server in order to load the data in the first place.  It also has to be loaded in the write-only app server in order for that Hibernate session to work with it, which brings me to the point of this post: Is there any way that I can use <a href="http://www.terracotta.org">Terracotta</a> to pool my Hibernate-managed objects into one pool used by both the read-only and write-only app servers?  I think I would like to pool my Hibernate second-level cache.  It seems like <a href="http://terracotta.org/confluence/display/explore/Hibernate">Terracotta can get the job done</a>.</p>
<p>That said, I&#8217;m anxious to explore this topic more when Orion Letizi from Terracotta Tech comes to speak at the <a href="http://www.bostonsug.org">Boston Scalability User Group</a> next Wednesday on May 28th.  The meeting starts at 6 p.m. at the IBM Innovation Center in Waltham, MA.  Of course there will be pizza and soda (sponsored by Terracotta &#8211; thank you!) so come to the meeting to have some food and explore what Terracotta can do for your project.  All the details, including directions, are on the <a href="http://www.bostonsug.org">Boston Scalability User Group</a> web site.  Remember, there will be pizza!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/2008/05/20/53/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Evil Query</title>
		<link>http://blog.anthonychaves.net/2008/03/18/the-evil-query/</link>
		<comments>http://blog.anthonychaves.net/2008/03/18/the-evil-query/#comments</comments>
		<pubDate>Tue, 18 Mar 2008 16:21:47 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[criteria]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[query]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/java/2008/03/18/the-evil-query</guid>
		<description><![CDATA[Here is the use case: I want to search all of my bookmarks by multiple tags with an &#8220;and&#8221; operation.  That means I want to see bookmarks that are tagged with Java AND JavaEE, not just Java and not just JavaEE.  I want to see bookmarks tagged with C# AND 3.5 not just [...]]]></description>
			<content:encoded><![CDATA[<p>Here is the use case: I want to search all of my bookmarks by multiple tags with an &#8220;and&#8221; operation.  That means I want to see bookmarks that are tagged with Java AND JavaEE, not just Java and not just JavaEE.  I want to see bookmarks tagged with C# AND 3.5 not just C# and not just 3.5.  For most of the project I&#8217;m using Hibernate and the Criteria API with a lot of success.  In this case I was not able to massage Hibernate into creating the correct query and that&#8217;s fine.  Hibernate has its uses and it is acceptable to write custom SQL when the need arises.  I wrote a method to build up the query and execute it.  Here is part of the method that builds up the query:</p>
<table bgcolor="#ffffff" border="0" cellpadding="3" cellspacing="0">
<tr>
<td align="left" nowrap="nowrap" valign="top"><code><font color="#000000">StringBuilder sqlString =<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong>new </strong></font><font color="#000000">StringBuilder</font><font color="#000000">(<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#2a00ff">                                "select distinct(b.id)<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#2a00ff">from bookmarks b "</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>int </strong></font><font color="#000000">counter = </font><font color="#990000">0</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>for</strong></font><font color="#000000">(</font><font color="#000000">Tag tag : tags</font><font color="#000000">) {</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">String alias = </font><font color="#2a00ff">"j" </font><font color="#000000">+ Integer.toString</font><font color="#000000">(</font><font color="#000000">counter</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">String joinFragment = </font><font color="#2a00ff">" inner join bookmarks_to_tags {alias}<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#2a00ff"> on b.id = {alias}.bookmark_id<br />
</font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#2a00ff"> and {alias}.tag_id = ? "</font><font color="#000000">;</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">joinFragment = joinFragment.replace</font><font color="#000000">(</font><font color="#2a00ff">"{alias}"</font><font color="#000000">, alias</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">sqlString.append</font><font color="#000000">(</font><font color="#000000">joinFragment</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">      </font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#7f0055"><strong> </strong></font></code><code><font color="#000000">counter++;</font><br />
<font color="#ffffff">    </font><font color="#000000">}</font></code></td>
</tr>
</table>
<p>Right away it looks like this query will get very expensive for large tag sets.  For smaller sets the query performs in a reasonable amount of time but more than 12 tags makes the query take quite a while.  This is exacerbated by the fact that this query will be executed multiple times if there is a sufficiently large result set (default is over 25) to give the user a paginated view.  This totally flies in the face of being nice to the database.  Hammering it with a complex query over and over won&#8217;t do the users any favors.</p>
<p>What are my options here?  I can work on making this query less complex (something that I will certainly do), but there is still the problem that, no matter how nice this query becomes, the query will be executed over and over as a user pages through the results.  The complexity of the query and its repeated execution makes the result set a good candidate for caching.  Caching the result set in memory with a key tied to the user or their session will allow the expensive query to be executed once with the bookmarks stored in a cache.  When the user pages through the results the cache will be consulted first where the result set will be stored.  I&#8217;m in the process of selecting a caching package for this project so I will post my selection and integration process soon.</p>
<p>Until then, I&#8217;d be happy to hear any suggestions on making this query a little nicer while preserving its &#8220;andness&#8221;.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/2008/03/18/the-evil-query/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Spring Web MVC and the benefits of pagination</title>
		<link>http://blog.anthonychaves.net/2008/03/14/spring-web-mvc-and-the-benefits-of-pagination/</link>
		<comments>http://blog.anthonychaves.net/2008/03/14/spring-web-mvc-and-the-benefits-of-pagination/#comments</comments>
		<pubDate>Fri, 14 Mar 2008 08:05:06 +0000</pubDate>
		<dc:creator>Anthony Chaves</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[architecture]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[scalability]]></category>
		<category><![CDATA[spring]]></category>
		<category><![CDATA[criteria]]></category>
		<category><![CDATA[mvc]]></category>
		<category><![CDATA[pagination]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[web]]></category>

		<guid isPermaLink="false">http://blog.anthonychaves.net/java/2008/03/14/spring-web-mvc-and-the-benefits-of-pagination</guid>
		<description><![CDATA[Lately I&#8217;ve been working on a project that keeps my web bookmarks sync&#8217;d across multiple computers and different web browsers.  The project is implemented in Java using JBoss 4.2.2, MySQL 5.0 and Spring 2.0.  It&#8217;s not a very big project right now, only about 40 class files and some small number of configuration [...]]]></description>
			<content:encoded><![CDATA[<p>Lately I&#8217;ve been working on a project that keeps my web bookmarks sync&#8217;d across multiple computers and different web browsers.  The project is implemented in Java using JBoss 4.2.2, MySQL 5.0 and Spring 2.0.  It&#8217;s not a very big project right now, only about 40 class files and some small number of configuration files, but the amount of data the application deals with can get fairly large.<br />
<span id="more-48"></span><br />
Since this app isn&#8217;t the only way to manage ones bookmarks that everyone has used since the dawn of the web, a feature to import bookmarks from browsers was very high on the to-do list.  So across the set of different browsers I&#8217;ve used on the different computers, and the backed up bookmarks that survived system formats I have around 700 bookmarks in total.  That probably isn&#8217;t a whole lot, but that many spread across thousands of users can add up quickly.</p>
<p>I used jhat to find out that each bookmark object uses 1.5 kilobytes of memory.  It doesn&#8217;t take much to see that a LOT of bookmarks will fit into 8 gigs of memory on a medium sized server.  It would take hundreds of thousands of users to generate enough bookmark data to fill that memory.  At this point it&#8217;s clear the application server box can handle the data load (that isn&#8217;t to say that it can handle the network load, however).</p>
<p>I spent some time coming up with a nice data model and ORM design so accessing the data once it&#8217;s in memory is lightning fast, maybe even faster.  But what about when it&#8217;s on a hard disk, deep in the database?  Dragging my 700 bookmarks out of the database when I only want to view the 25 most frequently visited is a bit overkill, especially when you consider dragging everyone else&#8217;s bookmarks out at the same time.  Even though this is still a small-time application I decided to be nice to the database anyway and implement some kind of paging solution.  Spring Web MVC doesn&#8217;t offer much that I&#8217;m aware of in regard to pagination help so it looks like I&#8217;m on my own.</p>
<p>Pagination is something that must take place in the model with the help of the view.  Right away we&#8217;ve got two things to deal with, though it&#8217;s best to deal with them in isolation.  The first thing I did was get the model right.  I&#8217;m using Hibernate for ORM and the Criteria API makes it easy to set offsets and limits.  That is, I can ask for the 50 next items (limit) starting at row 20 (offset).</p>
<p>The Criteria API is great at getting your data into a workable format without HQL or SQL (not that either of them is a bad thing.  I enjoy working with them).  Let&#8217;s take a look at the method that gets some new bookmarks.</p>
<p class="java" align="left">
<table bgcolor="#ffffff" border="0" cellpadding="3" cellspacing="0">
<tr>
<td align="left" nowrap="nowrap" valign="top"><code><br />
<font color="#ffffff">  </font><font color="#7f0055"><strong>public </strong></font><font color="#000000">List&lt;Bookmark&gt; getNewestBookmarks</font><font color="#000000">(</font><font color="#7f0055"><strong>int </strong></font><font color="#000000">offset, </font><font color="#7f0055"><strong>int </strong></font><font color="#000000">numBookmarks</font><font color="#000000">) {</font><br />
<font color="#ffffff">    </font><font color="#000000">DetachedCriteria c = DetachedCriteria.forClass</font><font color="#000000">(</font><font color="#000000">Bookmark.</font><font color="#7f0055"><strong>class</strong></font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#000000">c.addOrder</font><font color="#000000">(</font><font color="#000000">Order.desc</font><font color="#000000">(</font><font color="#2a00ff">"createdDate"</font><font color="#000000">))</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>return </strong></font><font color="#000000">getHibernateTemplate</font><font color="#000000">()</font><font color="#000000">.findByCriteria</font><font color="#000000">(</font><font color="#000000">c, offset, numBookmarks</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">  </font><font color="#000000">}</font></code></td>
</tr>
</table>
<p>So this method gets the model for us, but where do the offset and limit come from?  From the controller, of course.  This method is called from the controller like so:</p>
<p class="java" align="left">
<table bgcolor="#ffffff" border="0" cellpadding="3" cellspacing="0">
<tr>
<td align="left" nowrap="nowrap" valign="top"><code><br />
<font color="#ffffff">  </font><font color="#7f0055"><strong>public </strong></font><font color="#000000">ModelAndView newest</font><font color="#000000">(</font><font color="#000000">HttpServletRequest request, HttpServletResponse response</font><font color="#000000">) {</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>int </strong></font><font color="#000000">numberOfBookmarks = requestUtils.getSafeValueFromRequest</font><font color="#000000">(</font><font color="#000000">request, </font><font color="#2a00ff">"numberOfBookmarks"</font><font color="#000000">, </font><font color="#990000">250</font><font color="#000000">, defaultNumberOfNewest</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>int </strong></font><font color="#000000">offset = requestUtils.getSafeValueFromRequest</font><font color="#000000">(</font><font color="#000000">request, </font><font color="#2a00ff">"offset"</font><font color="#000000">, </font><font color="#990000">300</font><font color="#000000">, </font><font color="#990000">0</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><br />
<font color="#ffffff">    </font><font color="#000000">List bookmarks = bookmarkService.getNewestBookmarks</font><font color="#000000">(</font><font color="#000000">offset, numberOfBookmarks</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><br />
<font color="#ffffff">    </font><font color="#000000">Map model = requestUtils.generateBookmarkModel</font><font color="#000000">(</font><font color="#000000">offset, numberOfBookmarks, </font><font color="#990000">300</font><font color="#000000">, bookmarks</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#000000">model.put</font><font color="#000000">(</font><font color="#2a00ff">"bookmarkListName"</font><font color="#000000">, </font><font color="#2a00ff">"Newest Bookmarks"</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">    </font><font color="#7f0055"><strong>return new </strong></font><font color="#000000">ModelAndView</font><font color="#000000">(</font><font color="#2a00ff">"index"</font><font color="#000000">, model</font><font color="#000000">)</font><font color="#000000">;</font><br />
<font color="#ffffff">  </font><font color="#000000">}</font></code></td>
</tr>
</table>
<p>After sanity checking the offset and limit received from the request they are passed on to the getNewestBookmarks method to create and execute a somewhat unsightly query.  At this point, rather than gathering up all 700 of my bookmarks only 25 or so are returned.  What if I one day want to view the top 50 most visited bookmarks though?  Or what if I want to scroll through them one page at a time?  Passing in 0, 25 for the offset and limit of every method call won&#8217;t cut it.  The view needs to tell us how many bookmarks to retrieve and where to start the search.</p>
<p>Let&#8217;s say I&#8217;m viewing the top 25 bookmarks in my collection and I want to view 26-50.  Somehow the view has to tell the controller to tell the model about the  desired offset and limit.   This can be done by passing them in as request parameters.  In the code sample above, the HttpServletRequest object passed in to the controller contains values for offset and limit (numberOfBookmark) variables.  The JSP code below shows how to pass these parameters when a user clicks a link. (Actually I&#8217;m having a hard time getting the JSP to show up right when the code is rendered as HTML.  Just click the link and view the entire file instead.)</p>
<p><a href="http://blog.anthonychaves.net/wp-content/uploads/2008/03/operations.jsp" title="operations">JSP Pagination Operations</a></p>
<p>This page uses the JSTL core tag library to build the links that display the &#8220;previous/next/more/less&#8221; options.  Right after the body tag there is code to create four URLs, one for each action.  Each one starts off with a name and a value.  c:url var=&#8221;viewMoreUrl&#8221; value=&#8221;" creates a page-scoped variable called viewMoreUrl.  This variable will later be used to display the link that allows me to display five more bookmarks when I view the list.  The value is left blank which tells the tag to use the current base URL in creating the new one.  If I&#8217;m on a page that is located at /bookmarks/pages/viewUserBookmarks.html the new URL will start off as /bookmarks/pages/viewUserBookmarks.html.</p>
<p>Adding parameters to a URL is easy.  Using the c:param tag we get c:param name=&#8221;numberOfBookmarks&#8221; value=&#8221;${numberOfBookmarks + 5}&#8221;.  This tag appends a variable called numberOfBookmarks to the base URL described in the previous paragraph.  The value is a JSP expression that uses the numberOfBookmarks value passed in the model and increases it by five to pass back in the request.  Because we want the link only to display five more bookmarks and not change the offset that value will remain the value passed out in the model.  That gives us a URL that looks like /bookmarks/pages/viewUserBookmarks.html?numberOfBookmarks=30&amp;offset=0.</p>
<p>When this URL is accessed the request values for offset and numberOfBookmarks are exposed in the HttpServletRequest object for use by the controller.  When the getNewestBookmarks method is called the offset and limit are 0 and 30 instead of 0 and 25.</p>
<p>Obtaining a new offset value is done like getting a new limit value.  We start off with the same base URL and leave the numberOfBookmarks value unchanged.  We need to change the offset value though. c:param name=&#8221;offset&#8221; value=&#8221;${offset + numberOfBookmarks}&#8221; creates the offset value of the URL.  If we are viewing the newest bookmarks (offset 0)  and we are viewing 30 bookmarks when we click the link we should see bookmarks 31-60.</p>
<p>Here is an interesting thing to consider: what if a user goes to the second page using the default values.  The user will be viewing bookmarks 26-50.  The user then clicks the &#8220;view more&#8221; link three times and is now viewing 40 bookmarks.  The &#8220;previous&#8221; will now request that the offset be -15.  Unfortunately we can&#8217;t have negative bookmarks.  In this case the controller has some sanity checking built in to it.  The input parameter values must always be validated before they are used.  The controller uses a utility method to get a safe value for the offset and limit and ensures that neither can be a negative value.  This safe value is then passed back to the view once the model has been built.  Even though the offset in the request says -15 the controller will only go as low as 0.  More interesting work takes place in the generateBookmarksModel method, which I will talk about more next week.</p>
<p>What did I learn from making this effort?  Well, giving 8GB of RAM to an application server is a little overkill for a domain model like this.  The pagination that I came up with is still rudimentary, but a little more work could add more features, namely displaying page number links and the ability to change the number of bookmarks added at a time.</p>
<p>What is gained from this effort?  Being nice to the database opens up a few doors.  It dramatically improves scalability because it allows these requests to be stateless.  If an app server queried the database for all of my bookmarks then my session would have to be directed to that app server every time I made a request in order to not incur the cost of hitting the database again by another app server.  If round-robin load balancing is used the Hibernate session on each app server would contain a copy of my data and that&#8217;s a waste of resources, most notably it&#8217;s a hit to the database that didn&#8217;t have to happen.</p>
<p>Reducing the load on the database by getting only what we need and stateless sessions gives me the option of scaling the database with less difficulty.  A mostly-read application can get by with fewer hits to the write master and do a majority of their work from read-only slaves.   Spreading complex queries over multiple read slaves is much preferable to every app server hitting one database for every query.  By having stateless requests I can have more app servers (using less memory) hitting more database servers without maxing out the CPU utilization due to the cost of complex queries.</p>
<p>What should I do with the unused memory in the app server boxes?  Rather than give it to one app server instance for use as a cache it should be given to the pool of application servers for use as a cache.  One way to do that is with memcached.  I&#8217;ll talk about that more in the future too.</p>
<p>As always please leave comments.  I enjoy getting feedback on what I&#8217;ve written.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.anthonychaves.net/2008/03/14/spring-web-mvc-and-the-benefits-of-pagination/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
