31.7. Optimized Loading

The goal of optimized loading is to load the smallest amount of data required to complete a transaction in the fewest number of queries. The tuning of JBoss depends on a detailed knowledge of the loading process. This section describes the internals of the JBoss loading process and its configuration. Tuning of the loading process really requires a holistic understanding of the loading system, so this chapter may have to be read more than once.

31.7.1. Loading Scenario

The easiest way to investigate the loading process is to look at a usage scenario. The most common scenario is to locate a collection of entities and iterate over the results performing some operation. The following example generates an html table containing all of the gangsters:
public String createGangsterHtmlTable_none() 
    throws FinderException 
{
    StringBuffer table = new StringBuffer();
    table.append("<table>");

    Collection gangsters = gangsterHome.findAll_none();
    for (Iterator iter = gangsters.iterator(); iter.hasNext();) {
        Gangster gangster = (Gangster) iter.next();
        table.append("<tr>");
        table.append("<td>").append(gangster.getName());
        table.append("</td>");
        table.append("<td>").append(gangster.getNickName());
        table.append("</td>");
        table.append("<td>").append(gangster.getBadness());
        table.append("</td>");
        table.append("</tr>");
    }

    return table.toString();
}
Assume this code is called within a single transaction and all optimized loading has been disabled. At the findAll_none call, JBoss will execute the following query:
SELECT t0_g.id
    FROM gangster t0_g
    ORDER BY t0_g.id ASC
Then as each of the eight gangster in the sample database is accessed, JBoss will execute the following eight queries:
SELECT name, nick_name, badness, hangout, organization
  FROM gangster WHERE (id=0)
SELECT name, nick_name, badness, hangout, organization
  FROM gangster WHERE (id=1)
SELECT name, nick_name, badness, hangout, organization
  FROM gangster WHERE (id=2)
SELECT name, nick_name, badness, hangout, organization
  FROM gangster WHERE (id=3)
SELECT name, nick_name, badness, hangout, organization
  FROM gangster WHERE (id=4)
SELECT name, nick_name, badness, hangout, organization
  FROM gangster WHERE (id=5)
SELECT name, nick_name, badness, hangout, organization
  FROM gangster WHERE (id=6)
SELECT name, nick_name, badness, hangout, organization
  FROM gangster WHERE (id=7)
There are two problems with this scenario. First, an excessive number of queries are executed because JBoss executes one query for the findAll and one query to access each element found. The reason for this behavior has to do with the handling of query results inside the JBoss container. Although it appears that the actual entity beans selected are returned when a query is executed, JBoss really only returns the primary keys of the matching entities, and does not load the entity until a method is invoked on it. This is known as the n+1 problem and is addressed with the read-ahead strategies described in the following sections.
Second, the values of unused fields are loaded needlessly. JBoss loads the hangout and organization fields, which are never accessed. (we have disabled the complex contactInfo field for the sake of clarity)
The following table shows the execution of the queries:

Table 31.1. Un-optimized Query Execution

id name nick_name badness hangout organization
0 Yojimbo Bodyguard 7 0 Yakuza
1 Takeshi Master 10 1 Yakuza
2 Yuriko Four finger 4 2 Yakuza
3 Chow Killer 9 3 Triads
4 Shogi Lightning 8 4 Triads
5 Valentino Pizza-Face 4 5 Mafia
6 Toni Toothless 2 6 Mafia
7 Corleone Godfather 6 7 Mafia