Updating Entity (not EntityItem) with JPAContainer

Hi,

I would like to understand how, or if, it is possible to update an entity that is managed by a JPAContainer, by changing the entity’s fields rather than the properties of the EntityItem that contains it. I have created a JPAContainer using JPAContainer.make() and used it as the data source for a table. Now if I get one of the items in the table, it can be updated (both in the table and the database) as follows:

EntityItem<Person> personEntity = (EntityItem<Person>)ptTable.getItem(itemId);				
personEntity.getItemProperty("lastName").setValue("Smith");

But, for reasons of compatibility with existing code, what I would really like to do is this:

EntityItem<Person> personEntity = (EntityItem<Person>)ptTable.getItem(itemId);
person = personEntity.getEntity();
person.setLastName("Smith");

In this case the change is not reflected in the table and not persisted to the database. I have tried commit() on the EntityItem and JPAContainer, etc, but this doesn’t help.

Thanks,

Matthew Fleming

I’ve played with this a bit more.

If an entity is obtained from an EntityItem, using EntityItem.getEntity(), and then the value of one of the entity’s fields is changed, this change is reflected in the corresponding EntityItem property. But a Property.ValueChangeEvent does not fire. Whereas if getItemProperty().setValue() is called on the EntityItem, the EntityItem’s property will be changed in the same way, but a Property.ValueChangeEvent does fire. My surmise is that the JPAContainer listens for this event, and when it hears it, changes its record for the EntityItem and the record on disk. This is necessary because the relationship between a JPAContainer and an EntityItem it contains is more complicated than, for example, the relationship between a Collection and an Object it contains. In the latter case, there is only one copy of the Object in the Collection, so that if the Object is obtained from the Collection and then updated, the change is immediately reflected in the Collection. But each time a JPAContainer is asked for an EntityItem, it seems to produce a new EntityItem (with a unique hashcode, but the same itemId), so that a change to the EntityItem does not propagate automatically to the JPAContainer. The JPAContainer has to be notified to make the change itself (through the ValueChangeEvent, apparently).

Bottom line: this is a pain, or at least a problem if you’re trying to adapt existing code to use JPAContainer, as I am. Currently there does not seem to be any way of telling an EntityItem that its entity has changed, so that the change can be propagated to the JPAContainer and persisted, as it would be if setValue were called on an EntityItem Property. Might I request that this be added to the API?

Thanks,

Matthew Fleming

For anyone in a similar fix, this solves it, though it doesn’t win any style points. (Of course, the problem could be avoided entirely if you were building from the ground up for JPAContainer).


public static <T> void updateItemForEntity(T entity, EntityItem<T> item)  {
		java.lang.reflect.Field[] fields = entity.getClass().getDeclaredFields();
		for (java.lang.reflect.Field field : fields) {
			String propertyName = field.getName();
			try {
				Object propertyValue = field.get(entity);
				item.getItemProperty(propertyName).setValue(propertyValue);
			}
			catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

public static <T> void updateItemForEntity(T entity, EntityItem<T> item)  {
		Class entityClass = entity.getClass();
		java.lang.reflect.Field[] fields = entityClass.getDeclaredFields();
		for (java.lang.reflect.Field field : fields) {
			String propertyName = field.getName();
			String getterName = "get" + propertyName.substring(0,1).toUpperCase() + propertyName.substring(1);
			try {
				java.lang.reflect.Method method = entityClass.getMethod(getterName, null);
				Object propertyValue = null;
				propertyValue = method.invoke(entity);		
				if (propertyValue != null) item.getItemProperty(propertyName).setValue(propertyValue);
			}
			catch (Exception e) {e.toString();}
		}	
	}

Hi,
I found an alternative solution to this. As I am new to Vaadin and to Java, it’s entirely possible this solution is flawed in some way, but I think it achieves what you wanted.

// personList is the JPAContainer for Person
EntityManager entityManager = personList.getEntityProvider().getEntityManager();
Person thisPerson = entityManager.find(Person.class, itemId);

entityManager.getTransaction().begin();
// Here you can use all the setters defined for the class
thisPerson.setName(“New Name”);
entityManager.getTransaction().commit();
// JPAContainer needs to be refreshed to update display
taskList.refreshItem(itemId);

Regards,
Julian

Nice. I just returned to this project after a long hiatus, came up with the same solution, and then noticed your post. The essential element is that you’ve got hold of an entity manager which you use to get an entity. The entity is then managed, so any changes to it will be saved to the database. You don’t actually have get the entity manager from the JPAContainer. You can get it from Persistence.createEntityManagerFactory().createEntityManager() (which is what I did) or by injection.

I agree this is a better solution, thanks much.

Matthew Fleming

I am having the same problem with my code. In my case is a little bit more complicated, because I extract the entity from the item and used a FieldGroup to populate and modify the values.

After I modify the values, the entity reflected the new values, but I can’t get an update in the container. So, after the modification of the item I call again:

[font=courier new]
jpaContainer.addEntity(item.getEntity())
[/font]

Calling again with the same id doesn’t add a new entity, but it updates the values on the database.

EntityItem#refresh() reloads it from the database.