I had been mystified by what exactly the magic incantation -javaagent:toplink-essentials-agent.jar does for JPA clients, and my experiments had been inconclusive. I finally figured it out. Here is the answer in terms that Elvis can understand.

???When you start a JPA client application, tutorials such as this one tell you to use this mysterious incantation on the command line:

java -javaagent:toplink-essentials-agent.jar client.Client

What is the reason behind that? Go to the GlassFish forum and search for javaagent. You'll get lots of confused queries. (Ok, most of them are mine.) The gist of the friendly advice that I got seemed to be that the toplink-essentials-agent.jar file was required for lazy loading.

Lazy loading means that you can fetch an object from a database without also fetching all of its dependent objects. Here is the canonical example:

In a multiple-choice testing application, a Question has a number of choices:

public class Question implements Serializable {
    private int id;
    private Collection<Choice> choices;
    . . .
    public Collection<Choice> getChoices() {
        return choices;

Now let's say we load a Question object from the database. Should all choices be loaded as well? And what about the objects that are related to Choice objects? That can be dangerous--suppose a Choice remembers all the questions that contain it? Clearly, something has to give, or we pull out a potentially huge transitive closure with every query.

That's where lazy loading comes in. You can mark relationships so that they are fetched lazily. By default, collections are always fetched lazily. For example, consider this code:

Question q = em.find(Question.class, id);

It prints

{IndirectList: not instantiated}
elvis.entity.Choice[id=1,text=run anywhere]

Clearly, our collection isn't a humble array list. Indeed, calling q.getChoices().getClass() reveals that it is a oracle.toplink.essentials.indirection.IndirectList.

Ok, so I tried omitting -javaagent. What would happen to my lazy collection? Would it be eagerly fetched? Would my keyboard melt into a puddle of plastic if I accessed it? To my surprise, nothing special happened. The output was exactly the same. What, if anything, did the agent do?

???I finally figured it out, thanks to reading this article very carefully. You need -javaagent for one specific purpose, to lazily load one-to-one and many-to-one relationships. (If you knew this and you read this far, I am sorry to have wasted your time. This blog entry is for Elvis, not Einstein.)

We had a one-to-many relationship, so the agent didn't care. Let's add a lazily fetched one-to-one relationship:

public class Question implements Serializable {
    . . .
    private Choice answer;

    public Choice getAnswer() {
        return answer;
    . . .
    public String toString() {
        return getClass().getName() + "[id=" + id 
                + ",answer=" + answer"]";

Here we need to specify the fetch type since one-to-one relationship are eagerly fetched by default.

Now we'll be able to tell the difference between running with -javaagent and without.

Question q = em.find(Question.class, id);
With -javaagent Without -javaagent
elvis.entity.Choice[id=1,text=run anywhere]
elvis.entity.Question[id=3,answer=elvis.entity.Choice[id=1,text=run anywhere]
elvis.entity.Choice[id=1,text=run anywhere]

Both outputs are interesting.

???With -javaagent, the answer instance field is null, but the getAnswer method doesn't simply return the instance field (even though our code says it does). The agent intercepted the loading of the Question class and edited the bytecodes of the getAnswer method so that it fetches the result. (This process of on-the-fly bytecode modification is called "weaving" by the cognoscenti.)

Without -javaagent, lazy fetching has been disabled.

Why was the agent necessary? With the Collection<Choice> field, the persistence provider can simply set an object of a class that implements the Coll ection interface and that does the lazy loading. There is no need to modify the bytecodes of the class. But one can't do that with a single object. The Choice field must be set to a Choice object (or to null), and the getter must be modified. Mystery solved.

Unfortunately, I haven't yet found a way to actually see the modified bytecodes, but if you set a breakpoint in the debugger and inspect the Question object in the debugger, you can see a field that was added to the class:


???Here is the executive summary in terms that Elvis can understand: