@Stateless or @Singleton instead of static helper class?
Asked Answered
C

2

7

I'm maintaining some older JEE code which runs fine but is using some static helper classes where an entity manager is passed in the methods from the calling EJB(s) like this:

public class StaticHelper {

    public static void helpingOut(EntityManager entityManager, String value) {
        // i.e. insert value
    }
}

Since this doesn't seem to fit JEE very well and is not nice to unit-test, I've converted these helpers to @Stateless EJBs like so:

@Stateless
public class StatelessHelper {

    @PersistenceContext(unitName="SuperUnit")
    private EntityManager entityManager;

    public void helpingOut(String value) {
        // i.e. insert value
    }
}

Like that I can inject a mocked helper in the calling EJB with CDI-Unit.

Now, depending on the load, 1-3 instances of that stateless helper is created by the container which isn't a problem at all I would say, but anyway I thought about a @Singleton using either @ConcurrencyManagement(ConcurrencyManagementType.BEAN) or @Lock(LockType.READ) to make it multithreaded - but this doesn't seem to be a good idea since EntityManager is not thread-safe. Or does this explained here still apply?

"...The container serializes calls to each stateful and stateless session bean instance. Most containers will support many instances of a session bean executing concurrently; however, each instance sees only a serialized sequence of method calls. Therefore, a stateful or stateless session bean does not have to be coded as reentrant..."

Canopy answered 9/10, 2017 at 21:35 Comment(3)
You're not creating the EntityManager instance yourself. The container does that. So the citation doesn't apply on it at all.Denice
Your @Stateless bean is simple and inherently thread safe. You have nothing to gain by looking at more complex solutionsDiaphoretic
just out of curiosity: what do you mean by "this [...] is not nice to unit-test"? pure static functions (not depending on any external state, only their args) are IMHO testability nirvana, no?Procryptic
A
1

Business methods in Java EE (or the more recent denomination Jakarta EE) should be implemented in @Stateless beans. That is what they are ment for. So the approach you just describe perfectly fits into the Java EE paradigm.

@Singletons are ment for instances containing application-wide state.

@Singleton for beans containing business methods are models from other techologies, like Spring or Guice. In those, the business methods are not synchronized, so you have to beware that every class level attribute must be thread safe. This is not Java EE model, in which one thread is assured to access one instance of a Session Bean at any specific time (by specification), and that's what makes it safe to use with EntityManager.

This doesn't happen with @Singletons, and so, to use them concurrently you have to tune them with @ConcurrencyManagement annotations.

What you just did is just right.

Clarification

It looks like I said Singleton Session Beans are not thread safe. What I ment is that concurrent access to the single instance is not allowed by default (the other way round as in Spring or Guice), and so they can be bottlenecks for business methods. To allow concurrent access you have to tune them with the aformentioned @ConcurrencyManagement.

Autotrophic answered 10/11, 2021 at 23:28 Comment(0)
L
0

I created a simple project for checking/testing how the container handles transactions in SLSB and Singleton. Cases I covered are:

  • Using @PersistenceContext EntityManager inside SLSB
  • Using directly a @Datasource inside SLSB
  • Using a @Datasource inside a @Singleton


Below the conclusions of the test.

EntityManager (DashboardEM)

  • EntityManager is reliable. With the default isolation level it is enough to avoid data inconsistent.
  • When a concurrent exception happens the container will rollback, so handle appropriately system exceptions in order to not lose data. In our case a OptimisticLockException is thrown so we resend the point to the dashboard.
  • A "copy" of EnityManager is injected to each SLSB instances by the container. Afterwards it is the EntityManager responsible for data consistency.
  • SLSB is safe in sense that container guarantees only one thread at time can execute a single instance (but different instances runs concurrently in separate threads)

@Singleton (DSSegmentSingleton)

  • Makes sense to use @Singleton only if you are using directly a source. With Lock.WRITE you increase the isolation levels between threads/instances to SERIALIZABLE.
  • It creates a bottleneck, all the threads (clients) have to wait after each other in order to execute the method.
  • Lose the benefit of having multiple stateless instances in the pool (implies that multiple clients can do stuff at the same time).
  • In case of @Singleton the execution time would increase with the increase of clients because each would wait on each other. For 100clients, the 100th client would wait 99 x single execution time.
  • In case of @Singleton the execution time increases as the number of concurrent clients goes up. For example: if 100 clients call the Singleton SLSB at the same time, the last client would have an execution time of ( 99 x execution time ).

Other Solutions when you don't have EntityManager

  • Use isolation level directly in DB (select ... for update). See DashboardDSSelectForUpdate
  • Using TransactionManagement(BEAN) and changing isolation level of the connection like conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); See: DashboardDSTxBean

See also

Lewd answered 24/10, 2017 at 10:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.