Vita Rara: A Life Uncommon

Updating a List of Domain Entities in Struts 2


Categories: | | |

I've been working on Quadran a lot, and I'm creating pages for editing lists of entities. The CRUD example from the Struts 2 Showcase application does this in a very simple manner. It recreates all of the entities every time you update the list. That's fine for an example but unusable in real life.

In real life we have to present entities from our database and then put the values back into the same entities, and update our database. We can't just re-create them.

The Entity

So, we'll start with a simple domain object and assume that we're using something like JPA or Hibernate to persist it via a Data Access Object (DAO), and that it contains a property called "id".

public class MyEntity {
	private String id, value;

	public String getId () { return id; }
	public void setId (String in) { id = in; }

	public String getValue () { return value; }
	public void setValue (String in) { value = in; }
}

This is a simple JavaBean that contains two properties id and value.

Our Struts 2 Action Class and JSP

In our action we will have a List and a Map. The map is the key to mapping the values back to our beans. Our action looks like this:


public class EditMyEntitiesAction extends ActionSupport implements Preparable {
	
	// Service Beans
	MyEntityDao myEntityDao = new MyEntityDao (); // A data access object for managing MyEntity objects.

	// Model Beans
	List<MyEntity> myEntities;
	Map<String,MyEntity> myEntitiesMap;

	public void prepare () {
		// Get the List of MyEntity objects from the datastore.
		this.setMyEntities (myEntityDao.findAll () );

		// Create a Map using this.myEntities as the basis for it keyed on myEntity.id.
		Map<String,MyEntity> this.myEntitiesMap = new HashMap ();
		for (MyEntity myEntity : this.getMyEntities () ) {
			this.myEntitiesMap.put (myEntity.getId (), myEntity);
		}		
	}

	public String execute () throws Exception {
		// Iterate over the List of MyEntity objects and persist them using our DAO
		for (MyEntity myEntity : this.getMyEntities () ) {
			this.myEntityDao.updateDatabase (myEntity);
		}		
	}


	/* Accessors */

	public void setMyEntities (List<MyEntity> in) {
		this.myEntities = in;
	}

	public List<MyEntity> getMyEntities () {
		return this.myEntities;
	}

	public Map<String,MyEntity> getMyEntitiesMap () {
		return this.myEntitiesMap;
	}
}

That's all there is to the action. In the prepare we get the List of MyEntity objects and then put them into a map keyed on MyEntity.id. Pretty straight forward. The Map is the key to the whole process. We will use the OGNL EL notation to access that Map and fill the values back into it when a user submits the form.

Here's the pertinent section of the JSP:


<s:iterator value="myEntities">
    <s:textfield name="myEntitiesMap['%{id}'].value" value="%{value}" />
</s:iterator>

How OGNL Fills in our Domain Objects

In this we iterate over the myEntities List. On the textfield tag we set the name to "myEntitiesMap['%{id}'].value", this would result in something that looks like "myEntitiesMap['1'].value". When the user submits the form Struts2 will evaluate this name as so:

Expression Result
myEntitiesMap This will call getMyEntitiesMap() on our EditMyEntitiesAction.
['1'] This will call get("1") on the Map that was returned by getMyEntitiesMap(), which will return a MyEntity object.
.value Will call setValue() on the MyEntity object returned previously. Setting the value of the property to the value that was submitted by our user.

Basically myEntitiesMap['1'].value results in: EditMyEntitiesAction.getMyEntitiesMap().get ("1").setValue (<our user submitted value>).

It took me a while to figure out how to do this. I was struggling over how to get the values back into the "List" of entities in my action, then I hit on the idea of putting them into a Map keyed on the id of the entities, and voila, it all fits together.

What's Not Here

This is by no means an enterprise ready update mechanism. The primary things I've left out is validation. This can be added by implementing a validate() method in our Action. Best practice would be to delegate that validation to some other bean responsible for the management of MyEntity objects.

Reply

Please solve the math problem above and type in the result. e.g. for 1+1, type 2
  • Allowed HTML tags: <a> <img> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <pre> <h1> <h2> <h3>
  • Lines and paragraphs break automatically.
More information about formatting options