2 Questions on JPA Container Addon

Hi, I’m investigating jpa-container addon exploring jpa-container-demo app.


Question One

In CustomerWindow on save action the example close the window, so you can not save the entity twice,
in fact to update again the Item you have to reopen it again from table (jpacontainer).

On my application I’ll open “Windows” not as Window but as closable Tab, so the user can update the item more then once, in this case I got the following exception:

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.vaadin.addon.jpacontainer.demo.domain.Customer#6002]

Do I’missing something? is there any workaround? Ho to let update multiple times?


Question Two

My Application has say two tables Customer and MailSent (mails that system send are also stored into database), when the user update some Customer properties the system send an email to the user.
It was desirable to update MailSent table.
So how to say the MailContainer / MailProvider to update its items?

Regards

Hi is it possible no one could reply to my questions?
Are the questions not wel formed?, are they to hard to reply or they are to stupid?

Please help me, at least for the first question.

Is it the same item instance that you have taken from the table and update twice?
Did the entity exist in the database already before the first save operation?
Any more information that might be relevant?

This might be a problem in how you are using the container, or maybe you have stumbled across some bug - hard to tell, and I cannot test this right now. A small test case program could also get to the bottom of this faster.

The tables should use separate JPAContainer instances, but share the provider instance. In this case, updates should propagate automatically, at least on the level of whole items. The provider cannot propagate updates on as low a level as e.g. a container does to a table, though, so no per-property updates AFAIK.

Yes!

Yes! it is and Update and then update again

I’ll try to be helpful
I showed version field on the Object List and on the Object Form (opened as Window), as soon the object is updated (by calling item.commit()), the version field on the Object List is updated to new value, while not on the form.
I also printed the following walues, before and after the commit, but the value printed is unchanged.


              System.out.println("1 " + item.getItemProperty("version").getValue());
              System.out.println("2 " + form.getField("version").getValue());
              System.out.println("3 " + item.getEntity().getVersion());
              System.out.println("Commit");

              item.commit();

              System.out.println("1 " + item.getItemProperty("version").getValue());
              System.out.println("2 " + form.getField("version").getValue());
              System.out.println("3 " + item.getEntity().getVersion());

Basically what I did is to keep the jpacontainer demo as-is, just changed the behavior of the form, that after commit was called does not close the window, so the user can update the object more than once (without re-opening again from the Object List), and obviously I changed Business Objects,.

I’ll report much representative codes
GuestBookList

public class GuestBookList extends BaseWindow {

  private final Table table = new Table();
  final Button openButton = new Button("Open");

  @Resource(name = "guestBookProvider")
  private EntityProvider<GuestBook> entityProvider;
  private JPAContainer<GuestBook> container = new JPAContainer<GuestBook>(GuestBook.class);

  public GuestBookList() {
    setCaption("GuestBook");
    setIcon(new ThemeResource("icons/16/comments.png"));
  }

  @PostConstruct
  public void init() {
    openButton.setIcon(new ThemeResource("icons/16/comment_edit.png"));
    openButton.addListener(new Button.ClickListener() {
      @Override
      public void buttonClick(final ClickEvent event) {
        final Object itemId = table.getValue();
        if(itemId != null) {
          final EntityItem<GuestBook> item = container.getItem(itemId);
          if(item == null) getWindow().showNotification("GuestBook deleted by another user");
          else getWindow().addWindow(new GuestBookForm(item));
        }
      }
    });
    getMenuBar().addComponent(openButton);

    container.setEntityProvider(entityProvider);
    container.setApplyFiltersImmediately(false);
    container.setAutoCommit(true);
    container.setContainsIdFiresItemSetChangeIfNotFound(true);

    table.setSizeFull();
    table.setContainerDataSource(container);
    table.setVisibleColumns(new String[] { "version", "id", "firma", "data", "descrizione" });
    table.setColumnHeaders(new String[] { "version", "ID", "Firma", "Data", "Descrizione" });
    table.setColumnCollapsingAllowed(true);
    table.setColumnReorderingAllowed(true);
    table.setSelectable(true);
    table.setImmediate(true);

    addComponent(table);
  }

}

GuestBookForm


public class GuestBookForm extends Window {

  public GuestBookForm(final EntityItem<GuestBook> item) {
    setCaption(item.getEntity().toString());
    setIcon(new ThemeResource("icons/16/comment_edit.png"));
    item.setWriteThrough(false);
    setModal(true);

    final Form form = new Form();
    {
      form.setWriteThrough(true);
      form.setItemDataSource(item);
      form.setVisibleItemProperties(new String[] { "id", "version", "firma", "data", "descrizione" });
      addComponent(form);
      final Button save = new Button("Save", new Button.ClickListener() {
        @Override
        public void buttonClick(final ClickEvent event) {
          try {
            form.validate();

            try {
              System.out.println("1 " + item.getItemProperty("version").getValue());
              System.out.println("2 " + form.getField("version").getValue());
              System.out.println("3 " + item.getEntity().getVersion());
              System.out.println("Commit");

              // This throws 
              // javax.persistence.OptimisticLockException: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [it.oredelmondo.webstore.domain.GuestBook#1]

              item.commit();

              System.out.println("1 " + item.getItemProperty("version").getValue());
              System.out.println("2 " + form.getField("version").getValue());
              System.out.println("3 " + item.getEntity().getVersion());

              System.out.println();
              if (item.getItemId() == null) item.getContainer().addEntity(item.getEntity());

            } catch (final Exception e) {
              form.getParent().getWindow().showNotification("Meaage: ", e.getMessage(), Notification.TYPE_ERROR_MESSAGE);
            }
          } catch (final InvalidValueException e) {}
        }
      });
      save.setIcon(new ThemeResource("icons/16/disk.png"));
      form.getFooter().addComponent(save);
    }

    setSizeUndefined();
    setWidth("80%");
  }
}

The reported behavior affect the JPA-container demo app to.

The following is CustomerWindow class,
if you comment out the following ((Window) getParent()).removeWindow(CustomerWindow.this);
and add the following on the costructor setClosable(true);


  public CustomerWindow(final EntityItem<Customer> customer) {
    super("Customer Details");
    setClosable(true);
    setClosable(true);
.................
.................
.................
            buttons.setSpacing(true);
            Button applyBtn = new Button("Apply and Close", new Button.ClickListener() {

                public void buttonClick(ClickEvent event) {
                    try {
                        generalForm.validate();
                        billingForm.validate();
                        shippingForm.validate();
                        try {
                            customer.commit();

                            if (customer.getItemId() == null) {
                                // We are adding a new customer
                                customer.getContainer().addEntity(customer.getEntity());
                            }

                             // Just comment the following line
                            //((Window) getParent()).removeWindow(CustomerWindow.this);
                        } catch (Exception e) {
                            showNotification("Could not apply changes",e.getMessage(),Notification.TYPE_ERROR_MESSAGE);
                        }
                    } catch (InvalidValueException e) {
                        // Ignore it, let the form handle it
                    }
                }
            });
            buttons.addComponent(applyBtn);

The window will not be closed after custemer.commit() is executed and you can close the window manually.
You can try with autocommit on true and false, and follow this test case:

  1. Open the Customer N°1
  2. Change its name
  3. Press save
  4. Change its name againg
  5. Press save again

With this code I solved partially, also saving again and again when the opened object was previously created.

It works like this:

  • (First Use Case “Laod, Update, Update, …”) after the commit is performed the provider Update entity then I get again the Item from the container and set the version value to first EntityItem. This Works

  • (Second Use Case "Create, Update, …) First create the Object, and Then Update it. This does not works, infact when the object was created I have NOT the ID to reload again the Entity from the provider and cannot refresh the version value, also the ID property and the getItemId() method return null
    so the update after the save does not works, infact it add again the object to the provider.

   
   form.setWriteThrough(true);
   item.setWriteThrough(false);
   ..................
   ..................
   ..................
    save = new Button("Save", new Button.ClickListener() {
      public void buttonClick(final ClickEvent event) {
        try {
          form.validate();
          try {
            item.commit();

            if (item.getItemProperty("id").getValue() == null) item.getContainer().addEntity(item.getEntity());
            else item.getItemProperty("version").setValue(item.getContainer().getItem(item.getItemId()).getItemProperty("version"));

Can someone help me to hack the second use case?

Regards