I am trying to reorganize an old application to use EJB3 with JPA.
We have two client layers (one based on a servlet, one not), which are both called on the delegate layer, which calls the EJB layer, which in turn calls the DAO. EJB is EJB2 (bean-set storage), and the DAO uses manual SQL queries that complete transactions and close connections manually.
I want to replace EJB2 with EJB3 and change all DAOs to use JPA.
I started by replacing EJB2 code with EJB3 using container-driven transactions. Since the criteria for hibernation is so simple, and the EntityManager can be entered, I can do something like this:
@Stateless public class NewSelfcareBean implements SelfcareTcApi { @PersistenceContext(unitName="core") EntityManager em; public BasicAccount getAccount(String id) { Criteria crit = getCriteria(BasicAccount.class); crit.add(Restrictions.eq("id", id)); BasicAccount acc = (BasicAccount) crit.uniqueResult(); } }
No separate DAO layer required. The account object looks something like this:
@Entity @Table(name="er_accounts") public class BasicAccount { @OneToMany( mappedBy="account", fetch=FetchType.LAZY) protected List<Subscription> subscriptions; }
But in the servlet layer, where I call EJB to get the account object, I want to create a response that may (or may not include) child subscriptions from BasicAccount:
The servlet level is as follows:
ResponseBuilder rb; public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ... Account acc = getDelegateLayer().getAccount(); rb.buildSubscriptionResponse(acc.getSubscriptions()); ... }
Obviously, this did not work, since the transaction and the entity-manipulator were closed by the moment of returning to the servlet level. I get a LazyInitializationException.
So, I see several options:
- ServletFilter for manual transaction management. This means that I am losing the benefits of EJB container managed transactions, right? In addition, I would have to implement another filtering mechanism on another client (and not at the end of the world).
- Use a stateful bean session instead, then I can use the extended persistence context. But I really don't need a stateful bean, because the data is not saved between transactions. Thus, this would create an unnecessary load on the server, and I would use Stateful for something that it was not intended for.
- Calling
Hibernate.init(acc.getSubscriptions()) - this will work, but it needs to be done in EJB. Suppose I reuse a bean for another client method that does not need subscriptions? Optional DB call. - Use EAGER FetchType for the object of my account. Bad for performance and creates unnecessary load on the database.
None of these options seem good.
Am I missing something? How to do it? I cannot be the first person to have this problem ...