0

The setup is like this:

Front-end: GWT using RequestFactory for sending data objects
Back-end:
Web-layer:
GWT server side code which has injected EJB
EJB-layer:
Stateless sesion beans:

Data access bean (DAB)=> has injected EntityManager for JPA operations and provides methods for merging and retrieving of entities
Facade bean (FB) => calls methods of DAB and is interface between EJB and web layer

When an entity object (lets say MyEntity) is to be saved after it has been modified at the client side, the flow is like this:
1. Initiated by the client
2. Server side GWT code is run and invokes the following methods:
3. The find() method looks up the instance of MyEntity using FB.findMyEntity() which calls DAB.findMyEntity() which in turn uses EntityManager to do the look-up. The find() method must be invoked as its part of RequestFactory flow in GWT.
4. The save() leads to FB.saveMyEntity() --> DAB.saveMyEntity() --> EntityManager.merge() and the changed entity object is persisted.

It is obvious that each of find() and save() methods run in different JPA transaction which is ineffective and bad design.

With regards to keeping the facade bean interface with simple look-up and saving methods:

  1. What is the best design to handle this situation? - preferably with having both method calls in one JPA transaction.
  2. What are alternatives? With pros and cons.

EDIT: Including simplified examples of the code for FB and DAB.

Facade bean (FB):

            @Stateless
            public class MyFacadeBean implements MyFacade{

                @EJB
                private DataAccessBean dab;

                @Override
                public void saveMyEntity(MyEntity entity) {
                    dab.saveMyEntity(entity);
                }

                @Override
                public void findMyEntity(int id) {
                    dab.saveMyEntity(id);
                }

            }

Data access bean (DAB):

        @Stateless
        public class DataAccesseBean implements DataAccessBeanInterface{

            @PersistenceContext
            private EntityManager entityManager;

            @Override
            public void saveMyEntity(MyEntity entity) {
                entityManager.merge(entity);
            }

            @Override
            public void findMyEntity(int id) {
                entityManager.find(MyEntity.class,id);
            }

        }
ps-aux
  • 11,627
  • 25
  • 81
  • 128
  • Please post the code of FB and DAB, at least the annotations, fields, and methods which are involved in your question. – Beryllium Aug 10 '13 at 20:35
  • Is `@EJB` the correct annotation for `entityManager`? – Beryllium Aug 10 '13 at 23:03
  • You are right. It was a mistake. I corrected it. Thanks. – ps-aux Aug 11 '13 at 06:52
  • You **should** use separate transactions, but you **must** run them in the same JPA session (same `EntityManager` instance, one per HTTP request) so that you have a single instance per entity. – Thomas Broyer Aug 12 '13 at 17:30

1 Answers1

1

In your web layer you have two calls to a stateless session bean, so this results in two transactions.

There are three options


  • Use a UserTransaction in your web layer code:
final InitialContext ic = new InitialContext();
final UserTransaction ut = (UserTransaction) ic.lookup("UserTransaction");
ut.begin();
try {
    // find
    // set new value in entity
    // save
    ut.commit();
    System.out.println("committed");
} catch (Exception ex) {
    ut.rollback();
    System.out.println("rolled back");
}
  1. This way the client controls the transaction.
  2. If someone forgets to use a UserTransaction, this will result in multiple transactions.
  3. There is no need to change the EJB side.

  • Add a method in the facade which calls both DAB methods on behalf of the client:
public void findAndSave(int id, String newValue, ...) {
  // find
  // set new value in entity
  // save
}
  1. This way the EJB controls the transaction.
  2. The facade simplifies the interface (n calls are reduced to 1)
  3. You possibly have to provide other methods as well on the EJB side. findAndSave is close to the database. I suggest using methods/names which correspond to the use case like updatePersonAddress for example.

  • Convert your facade into a stateful session bean

A quote from Enterprise Java Beans, 2nd edition, :

8.5.1. Transaction Propagation in Bean-Managed Transactions

With stateless session beans ... transactions that are managed using the UserTransaction must be started and completed within the same method ... In other words, UserTransaction transactions cannot be started in one method and ended in another.
...
With stateful session beans, however, a transaction can begin in one method and be committed in another because a stateful session bean is only used by one client. This allows a stateful session bean to associate itself with a transaction across several different client-invoked methods.

  1. You have to use a stateful session bean
  2. You have to use bean managed transacations
  3. Transaction control is spread over both client and EJB
  4. begin and commit are in different methods.

Experience told me that the 1st approach does not work, if performance matters.

So personally I prefer the 2nd approach: The control on the duration/scope of transactions is on the EJB side; the same applies to the number of SQL statements which hit the database. Clients only call one single method of the service/facade (for a single HTTP request for example).

Notes:

  • I have tested this setup using JBoss 6.1.1 with PostgreSql.
  • The only reliable way to double-check the number of transactions was to observe the PostgreSql log file.

Related:

Community
  • 1
  • 1
Beryllium
  • 12,808
  • 10
  • 56
  • 86
  • My problem here is that I cannot control the call to find() method. I only provide the method implementation. The same applies for save() method => So the `findAndSave()` method is not an option. Both methods are also part (at current design) of two different objects in web layer. GWT RequestFactory framework code controls the calls to this methods. According to your answer the solution (even if not ideal one) could be to: – ps-aux Aug 11 '13 at 07:01
  • 1. Obtain custom transaction in `find()` method and begin it. 2. "Store it"/provide access to the transaction instance to the second object which invokes `save()` 3. In the `save()` method use this transaction and commiting it at the end. Is that correct? – ps-aux Aug 11 '13 at 07:02
  • @LeNoob That's actually a 3rd option, using a SFSB. I've added it to the answer. – Beryllium Aug 11 '13 at 08:37
  • Actually I meant, getting the transaction instance from facade stateless bean to web layer servlet/dao object. Is that even possible in EJB? Using stateful session bean occured to me as the possible solution but I was not sure if it is the best or only solution. Based on what you mentioned, it actually seems to be THE BEST way to go - stateful session bean which commits transaction at the end of the session. – ps-aux Aug 11 '13 at 09:25
  • You mean explicitly "passing/exporting/returning/storing" a transaction context? A transaction context is propagated implicitly, it's possibly injected into SBs, and it's normally bound to a thread. In **your specific case** each approach has it's pros and cons. Try to implement the SFSB for a simple use case, and please report your result (nevertheless I suggest implementing the other options as well). – Beryllium Aug 11 '13 at 09:40