Wicket in Action

A comprehensive guide for Java developers building Wicket-based web applications

Building a smart EntityModel

A lot of Wicket applications use an ORM framework to work with the database. Because ORMs provide a generic mechanism for loading entities we can create a generic Wicket model that can simplify binding between ORM and Wicket. In this article I will walk you through creating an EntityModel that will make using Wicket and ORM fun, read inside…


In order to load an entity from the database we need two pieces of information: entity’s class and id. Since there is no standard way to get an id from an object we will create an interface that will standardize the process:

1
2
3
4
public interface Identifiable<T extends Serializable>
{
  T getId();
}

The simplest entity model can then look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public abstract class AbstractEntityModel<T extends Identifiable< ? >>
        extends
            LoadableDetachableModel<T>
{
    private Class clazz;
    private Serializable id;
 
    public AbstractEntityModel(T entity)
    {
        clazz = entity.getClass();
        id = entity.getId();
    }
 
    public AbstractEntityModel(Class< ? extends T> clazz, Serializable id)
    {
        this.clazz = clazz;
        this.id = id;
    }
 
    @Override
    protected T load()
    {
        return load(clazz, id);
    }
 
    protected abstract T load(Class clazz, Serializable id);
}
  • 26: the method used to perform the actual loading of entity from the ORM

This model can load any entity given an instance or a class and id, and will detach itself at the end of request. The model is abstract because the way to retrieve a handle to the ORM session varies from application to application. For example, in my project that uses Salve's dependency injection and Hibernate a concrete implementation will look like this:

1
2
3
4
5
6
7
8
9
10
11
public class EntityModel<T> extends AbstractEntityModel<T>
{
    @Dependency
    private Session session;
 
    @Override
    protected Identifiable load(Class clazz, Serializable id)
    {
        return session.get(clazz, id);
    }
}
  • 3: Salve annotation that will make sure field session is injected with the current hibernate session
  • 7-10: concrete implementation of the #load() method using hibernate Session

Handling Errors

There are cases when a user deletes an object from the database that another user is currently viewing in the UI. In this case when the UI tries to render and calls getObject() on our EntityModel it will return null. There is nothing worse in the log then a cryptic NullPointerExcepton. A much better approach is to check for this condition and throw a type of exception that can be intercepted and processed appropriately by the UI.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public abstract class AbstractEntityModel<T extends Identifiable< ? >>
        extends
            LoadableDetachableModel<T>
{
 
    @Override
    protected T load()
    {
        T entity = load(clazz, id);
        if (entity == null)
        {
            throw new EntityNotFoundException(clazz, id);
        }
        return entity;
    }
 
 }
  • 10-13: built-in null check for better error reporting

Then in our RequestCycle subclass:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyRequestCycle extends WebRequestCycle
{
    @Override
    public Page onRuntimeException(Page page, RuntimeException e)
    {
        if (e instanceof EntityNotFoundException)
        {
            return new EntityNotFoundErrorPage((EntityNotFoundException)e);
        }
        else
        {
            return super.onRuntimeException(page, e);
        }
    }
 
}

EntityNotFoundErrorPage can present a user-friendly message explaining why the error happened.

Using EntityModel to bind to Forms

Suppose the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class EditPersonPage extends WebPage
{
    public EditPersonPage(Long personId)
    {
        this(new EntityModel<Person>(Person.class, personId));
    }
 
    private EditPersonPage(IModel<Person> model)
    {
 
        Form<Person> form = new Form<Person>("form", new CompoundPropertyModel<Person>(model))
        {
            @Override
            public boolean process()
            {
                beginTransaction();
                if (super.process())
                {
                    commitTransaction();
                    return true;
                }
                else
                {
                    rollbackTransaction();
                    return false;
                }
 
            }
        };
 
        add(form);
 
        form.add(new TextField<String>("name"));
    }
}

When the form is submitted the following series of calls will be made:

  • Form#process()
    • beginTransaction()
    • super.proces()
      • Person p=EntityModel.getObject()
      • p.setName(name);
      • Form#onSubmit()
    • commitTransaction();
  • EntityModel#detach();

This is interesting because the Person entity is loaded and the TextField sets the value on the loaded Person inside the same transaction, which means this value will be persisted automatically by the ORM when the transaction ends. Any changes done to this form’s data are automatically reflected in the database without any extra code.

Our AbstractEntityModel is already good enough to handle this scenario, but what if we wanted to reuse our form to create a new entity instance instead of just editing one? If we had a way to do this we can then reuse any Form to create or edit an entity. To achieve this we need to add two things to our model:

  1. Ability to hold on to a transient entity instance (disable detaching while the entity is transient)
  2. Allow the model to intelligently begin detaching if a transient entity instance has become persistent between getObject() and detach()

Since it is not possible to override LoadableDetachableModel’s detach() we will have to convert our model to implement IModel directly – giving us full control:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
public abstract class AbstractEntityModel<T extends Identifiable< ? >> implements IModel<T>
{
    private final Class clazz;
    private Serializable id;
 
    private T entity;
 
    public AbstractEntityModel(T entity)
    {
        clazz = entity.getClass();
        id = entity.getId();
        this.entity = entity;
    }
 
    public AbstractEntityModel(Class< ? extends T> clazz, Serializable id)
    {
        this.clazz = clazz;
        this.id = id;
    }
 
    public T getObject()
    {
        if (entity == null)
        {
            if (id != null)
            {
                entity = load(clazz, id);
                if (entity == null)
                {
                    throw new EntityNotFoundException(clazz, id);
                }
            }
        }
        return entity;
    }
 
    public void detach()
    {
        if (entity != null)
        {
            if (entity.getId() != null)
            {
                id = entity.getId();
                entity = null;
            }
        }
    }
 
    protected abstract T load(Class clazz, Serializable id);
 
    public void setObject(T object)
    {
        throw new UnsupportedOperationException(getClass() +
                " does not support #setObject(T entity)");
    }
}
  • 6: cache for the entity instance that we keep for the request, only the first #getObject() call will populate it
  • 12: notice we initialize the cache if we have the instance
  • 25: guard so we only call #load() if we have a valid id
  • 39: guard from preventing detaching a detached model
  • 41: guard to make sure we do not null out a transient entity instance, we want to keep it until it is persisted
  • 43: if a transient instance was persisted keep its id so it can be reloaded
  • 44: now that we have a valid id we no longer need to keep the cache

From now on our EntityModel will hold on to the transient entity until it is persisted. If we subclass the Form in the previous example and override its onSubmit() to persist the entity the model will switch into persistent mode after the form is submitted. So, it doesn’t matter if the EntityModel given to that form contains a transient or a persistent instance of the entity, the model and therefore also the form will simply do the right thing.

That’s it for now. Perhaps if there is more interest in such models I will revisit this and add a couple of other useful features.

With this book, Wicket will become the greatest territory the Dutch have settled since Manhattan.

Nathan Hamblen
Senior Software Engineer, Teachscape Inc.

This is the complete and authoritative guide to Wicket, written and reviewed by the core members of the Apache Wicket team. If there's anything you want to know about Wicket, you are sure to find it in this book.

Jonathan Locke
Founder and Architect of Apache Wicket, Foreword Wicket in Action

Without question, Wicket in Action... is the be-all and end-all when it comes to Wicket.

Geertjan Wielenga, Wicket Netbeans Plugin Author

The tutorial and conversational tone of the writing makes the book very approachable.

Nick Heudecker
System Mobile

Loved the sample application—it tied everything together.

Phil Hanna
Senior Software Developer, SAS Institute

The essential guide for learning and using Wicket.

Erik van Oosten
Lead programmer and Project Manager, JTeam

Finally, the Web Framework of web frameworks, Apache Wicket, now has a bible of its own.

Per Ejeklint
Senior Software Architect, Heimore group

Wicket is an innovative evolution of the MVC programming with simple roots, but without a primer such as this, it can be more challenging than it needs to be.

Brian Topping
Founder, Bill2 Inc.

Wicket In Action glues the areas of web development with Apache Wicket together and gives a great overview of Apache Wicket...it will make a great compendium.

Nino Martinez Wael
Java Specialist, Jayway Denmark