Reputation: 3926
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..."
Upvotes: 7
Views: 648
Reputation: 1521
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.
@Singleton
s 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
.
Upvotes: 1
Reputation: 461
I created a simple project for checking/testing how the container handles transactions in SLSB
and Singleton
. Cases I covered are:
@PersistenceContext EntityManager
inside SLSB
@Datasource
inside SLSB
@Datasource
inside a @Singleton
Below the conclusions of the test.
EntityManager
is reliable. With the default isolation level it is enough to avoid data inconsistent. OptimisticLockException
is thrown so we resend the point to the dashboard. 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
only if you are using directly a source. With Lock.WRITE
you increase the isolation levels between threads/instances to SERIALIZABLE
.@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. @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 ). select ... for update
). See DashboardDSSelectForUpdate TransactionManagement(BEAN)
and changing isolation level of the connection like conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
See: DashboardDSTxBeanUpvotes: 0