Problem with JPAContainer and adding entity

Hello everyone,

I’m having some issues with JPAContainer and adding a new entity. The new entity does not contain the generated @Id.

This code:


@SuppressWarnings("serial")
public class VaadinFormTestApplication extends Application {

    private static final Logger LOG = LoggerFactory.getLogger(VaadinFormTestApplication.class);
    
    private static final String PERSISTENCE_UNIT = "FormTest";
    
	@Override
	public void init() {
		Window mainWindow = new Window("VaadinFormTest Application");
		setMainWindow(mainWindow);

        Label label = new Label("<h1>FormTest</h1>", Label.CONTENT_XHTML);
        mainWindow.addComponent(label);
        
        final JPAContainer<Person> container = JPAContainerFactory.make(Person.class, PERSISTENCE_UNIT);
        //container.setAutoCommit(false);
        
        final Form form = new BeanValidationForm<Person>(Person.class);
        form.setWriteThrough(false);
        form.setImmediate(true);
        form.setFormFieldFactory(new com.vaadin.addon.jpacontainer.fieldfactory.FieldFactory());
        form.setItemDataSource(container.getItem(container.addItem()));
        form.setVisibleItemProperties(new String[] {"name","phone","email","address"});
        form.getFooter().addComponent(new Button("Save", new Button.ClickListener() {
            
            public void buttonClick(ClickEvent event) {
                LOG.debug("     Entity before form commit: {}", ((JPAContainerItem<?>)form.getItemDataSource()).getEntity());
                form.commit();
                LOG.debug("Entity before container commit: {}", ((JPAContainerItem<?>)form.getItemDataSource()).getEntity());
                container.commit();
                LOG.debug(" Entity after container commit: {}", ((JPAContainerItem<?>)form.getItemDataSource()).getEntity());
            }
            
        }));
        
        mainWindow.addComponent(form);
        
	}

}

@Entity
public class Person {
    
    @Id
    @GeneratedValue
    private long id;
    
    @Size(min=2,max=32)
    private String name;
    
    @Size(min=2,max=32)
    private String phone;

    @Size(min=2,max=32)
    private String email;
    
    @NotNull
    @OneToOne
    private Address address;

    .... GETTERS AND SETTERS ....
    
    public String toString() {
        return id + ": " + name;
    }
    
}

Outputs the following exception to the browser:


javax.validation.ConstraintViolationException: Bean Validation constraint(s) violated while executing Automatic Bean Validation on callback event:'prePersist'. Please refer to embedded ConstraintViolations for details.
	org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener.validateOnCallbackEvent(BeanValidationListener.java:90)
	org.eclipse.persistence.internal.jpa.metadata.listeners.BeanValidationListener.prePersist(BeanValidationListener.java:62)
	org.eclipse.persistence.descriptors.DescriptorEventManager.notifyListener(DescriptorEventManager.java:698)
        .......
        com.example.vaadinformtest.VaadinFormTestApplication.init(VaadinFormTestApplication.java:40)
        .......

So it says it cannot save the item, when adding it to the form on line 40:

form.setItemDataSource(container.getItem(container.addItem()));

So, to remedy that, one could possibly use a Batchable JPAContainer… Testing this:


        final JPAContainer<Person> container = JPAContainerFactory.makeBatchable(Person.class, PERSISTENCE_UNIT);
        container.setAutoCommit(false);

Results in the following output when saving:


11847 [http-8080-3]
 DEBUG c.e.v.VaadinFormTestApplication -      Entity before form commit: 0: null
11851 [http-8080-3]
 DEBUG c.e.v.VaadinFormTestApplication - Entity before container commit: 0: My name
11927 [http-8080-3]
 DEBUG c.e.v.VaadinFormTestApplication -  Entity after container commit: 0: My name

As you can see, the ID of the Person is still 0. And I have verified that an entity has been saved to the database with a new ID.
The extra side-effect of this is that when clicking Save twice, 2 entities are created in the database.

Only way around this I’ve found is to not make JPAContainer batchable and to have validation-safe default values in the Person entity (i.e. Person.name = “123”).
But that creates a new Entity in the database even if the user does not click the save button.

So, why even use JPAContainer as a backend for Form? Because JPAContainers FieldFactory only works with a JPAContainer. And there’s no good alternative as far as I know.

I had thought that that would be possible somewhat like that. There is understandably a problem with the item ID, as it is the value of the @Id property of the entity. As the property isn’t set before the entity committed, there is a problem. There could be some solution.

Anyhow, the container buffering is not really that nice when adding items. If the container is bound to a table, the empty item is displayed in the table, which is not nice. It’s usually better to use a temporary item that is not added to the container before it is edited in the form. JPAContainer has a createEntityItem() method that creates an item for an entity, so that you can use the item in the form that you use also for editing items. The same pattern as is used in
this example with BeanItemContainer
.