5
votes

I have a Seam 3 sandbox application using JBoss 7, Hibernate as default JPA implementation and as JSF as web front end.

I have the problem, that the SQL UPDATE is swallowed by default.

My stateful EJB in conversation scope maintains an extended scoped EntityManager and one Entity, Container Managed Transactions (Requires new)

  1. The EntityManager gets injected
  2. The EJB uses the EM to load the Entity and keeps it in a field
  3. JSF application accesses the EJB and its entity, changes a String field
  4. JSF application calles "Save" method in EJB
  5. In save() I check, if the Entities field was changed -> it was changed properly
  6. I do nothing more, the container commits the transaction after save() is finished.
  7. Problem: No SQL update is performed against the DB.

If i extend save() by:

a) entityManager.contains(entity) the UPDATE is executed as expected (result is "true")

OR

b) entityManager.persist(entity) the UPDATE is executed as expected

Q: As far as I understand the specs neither of a) or b) should be required, because the Entity remains managed during the entire process. I dont understand, why a) has an effect on saving. I can imaging the b) has an effect on saving, but it should not be required, should it?

Any explanation is welcome.

Here is my EJB:

@Named
@ConversationScoped
@Stateful
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class LanguageBean {

    @PersistenceContext(type = PersistenceContextType.EXTENDED)
    private EntityManager em;
    @Inject
    private UserTransaction transaction;

    private Language value;

    @Inject
    Conversation conversation;

    public LanguageBean() {
        super();
    }

    @Begin
    public void selectLanguage(Long anId) {
        conversation.setTimeout(10 * 60 * 1000);
        if (anId != null) {
            value = em.find(Language.class, anId);
        }
    }

    @BeforeCompletion
    public void transactionComplete(){
        System.out.println("transactionComplete");
    }

    public Language getValue() {
        return value;
    }

    @Produces
    @Named
    @ConversationScoped
    public Language getLanguage() {
        return getValue();
    }

    public void setValue(Language aValue) {
        value = aValue;
    }

    @End
    public String save() {
//      displays the changed attribute:
        System.out.println("save code: "+value.getCode());

//      why is either this required:
//      boolean tempContains = em.contains(value);
//      System.out.println("managed: "+tempContains);

//      or: why is persist required:
        em.persist(value);
        return "languages?faces-redirect=true";
    }

    @End
    public String cancel() throws SystemException {
        transaction.setRollbackOnly();
        return "languages?faces-redirect=true";
    }

}
2
I found out entityManager.flush() also solves the problem. But I dont understand why this seems to be required. From the JPA spec: "When the JTA transaction commits, the provider must flush all modified entity state to the database."user1187037
maybe somewhere the hibernate session's FlushMode is set to none?Firo

2 Answers

1
votes

My experience is largely with seam-2 but should be equally applicable here.

The conversation and JPA session is decoupled in seam for the simple reason that a conversation ending may not result in the entity being saved.

For example, a cancel action on a long running conversation would end the conversation (since there is no reason to maintain the conversation anymore)

Considering that you are doing a rollback on the cancel in your example, it would also seem logical that you would need to call a flush as suggested by @user1187037 (theoretically a commit but I don't think that's allowed)

I think there might have been a configuration you could set so that it did flush on conversation end but I may be mistaken.

In any case, http://javalangblog.blogspot.co.uk/2010/04/flush-mode-conversation.html seems to suggest a solution

Hope that helps.

EDIT: You can configure the flush mode per conversation using xml

<begin-conversation join="true" flush-mode="COMMIT" />

and using annotations

@Begin(flushMode=COMMIT)

Bear in mind though that a conversation can @End without it being explicitly defined. If a user is partway through the conversation, made changes to entities and then abandons the conversation, it will get automatically closed after a timeout. If I remember correctly, this will cause any changes to get committed in the above case.

References:

http://docs.jboss.org/seam/3/persistence/3.0.0.Alpha1/reference/en-US/html_single/#d0e249 http://docs.jboss.org/seam/3/latest/api/org/jboss/seam/persistence/FlushModeType.html

0
votes

Try to add @Remove annotation on methods annotated by @End.

In my opinion @End annotation does not result in bean destruction. So the persistence context is active even after save() execution and its content cannot be flushed to database.