Design Generic CRUD Session Bean
Asked Answered
L

2

3

This question has been asked once here EJB 3 Session Bean Design for Simple CRUD, and I just want to ask more in depth questions about this design. I already tried to ask the questions at the original post, however, I did not see any respond, so I decide to create new post. So Pascal solution of implement generic CRUD session bean is as follow

public interface GenericCrudService {
    public <T> T create(T t);
    public <T> T find(Class<T> type, Object id);
    public <T> void delete(T t);
    public <T> T update(T t);
    public List findWithNamedQuery(String queryName);
    public List findWithNamedQuery(String queryName, int resultLimit);
    public List findWithNamedQuery(String namedQueryName, 
                                   Map<String, Object> parameters);
    public List findWithNamedQuery(String namedQueryName, 
                                   Map<String, Object> parameters,
                                   int resultLimit);
    public <T> List<T> findWithNativeQuery(String sql, Class<T> type);
}

AND

@Stateless
@Remote(GenericCrudService.class)
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class GenericCrudServiceBean implements GenericCrudService {
    @PersistenceContext
    private EntityManager em;

    @Override
    public <T> T create(T t) {
        em.persist(t);
        return t;
    }

    @Override
    public <T> T find(Class<T> type, Object id) {
        return em.find(type, id);
    }

    @Override
    public <T> void delete(T t) {
        t = em.merge(t);
        em.remove(t);
    }

    @Override
    public <T> T update(T t) {
        return em.merge(t);
    }

    @Override
    public List findWithNamedQuery(String queryName) {
        return em.createNamedQuery(queryName).getResultList();
    }

    @Override
    public List findWithNamedQuery(String queryName, int resultLimit) {
        return em.createNamedQuery(queryName).setMaxResults(resultLimit)
                .getResultList();
    }

    @Override
    public List findWithNamedQuery(String namedQueryName,
                                   Map<String, Object> parameters) {
        return findWithNamedQuery(namedQueryName, parameters, 0);          
    }

    @Override
    public List findWithNamedQuery(String namedQueryName,
                                   Map<String, Object> parameters,
                                   int resultLimit) {
        Query query = this.em.createNamedQuery(namedQueryName);
        if(resultLimit > 0) {
            query.setMaxResults(resultLimit);            
        }
        for (Map.Entry<String, Object> entry : parameters.entrySet()) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        return query.getResultList();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T>  List<T> findWithNativeQuery(String sql, Class<T> type) {
        return em.createNativeQuery(sql, type).getResultList();
    }
}

Questions:

1) When I try to reference this EJB bean in my managed bean, I have to do

@EJB
private GenericCrudService myEJB;

instead of

@EJB
private GenericCrudServiceBean myEJB;

This make little sense to me since GenericCrudService is just an interface, GenericCrudServiceBean is the Stateless Bean

I get this exception when I do GenericCrudServiceBean,

Caused by: javax.naming.NamingException: Lookup failed for 'java:comp/env/com.bridgeye.web.Profile/myEJB' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NamingException: Exception resolving Ejb for 'Remote ejb-ref name=com.bridgeye.web.Profile/myEJB,Remote 3.x interface =com.bridgeye.ejb.GenericCRUDServiceBean,ejb-link=null,lookup=,mappedName=,jndi-name=com.bridgeye.ejb.GenericCRUDServiceBean,refType=Session' .  Actual (possibly internal) Remote JNDI name used for lookup is 'com.bridgeye.ejb.GenericCRUDServiceBean#com.bridgeye.ejb.GenericCRUDServiceBean' [Root exception is javax.naming.NamingException: Lookup failed for 'com.bridgeye.ejb.GenericCRUDServiceBean#com.bridgeye.ejb.GenericCRUDServiceBean' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming} [Root exception is javax.naming.NameNotFoundException: com.bridgeye.ejb.GenericCRUDServiceBean#com.bridgeye.ejb.GenericCRUDServiceBean not found]]]
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:518)
at com.sun.enterprise.naming.impl.SerialContext.lookup(SerialContext.java:455)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at javax.naming.InitialContext.lookup(InitialContext.java:392)
at com.sun.enterprise.container.common.impl.util.InjectionManagerImpl._inject(InjectionManagerImpl.java:597)

2) what is the different between javax.rmi.Remote and javax.ejb.Remote? If I took out annotation Remote, does my EJB become local or I have to specify with annotation Local

3) In my managed bean, I do a simple myEJB.find(User.class, id), but if I have @TransactionAttribute(TransactionAttributeType.MANDATORY), then I run into exception, it work fine if I took out TransactionAttribute statement. Any idea why?

Laterality answered 12/4, 2011 at 20:33 Comment(0)
A
6
  1. The EJB container will actually create a proxy-based implementation of the interface that wraps your bean and adds things like transactions and security checks. This proxy is then injected into the annotated field, which is why it needs to have the interface type. I think in EJB 3.1, you can omit the interface and have only a bean class, and the container will create a subclass to implement its magic.
  2. javax.ejb.Remote is an EJB 3.0 annotation, while java.rmi.Remote is a plain interface from before EJBs existed. There is no javax.rmi.Remote. And you are right, if you do not use the Remote annotation, then by default the EJB will be considered to have only a local interface.
  3. From the API doc of TransactionAttributeType.MANDATORY:

    If a client invokes the enterprise bean's method while the client is associated with a transaction context, the container invokes the enterprise bean's method in the client's transaction context.

    If there is no existing transaction, an exception is thrown.

    You probably want to use TransactionAttributeType.REQUIRED, which automatically starts a transaction if none is present and is also the default if you use the annotation without a parameter.

Anthiathia answered 12/4, 2011 at 21:22 Comment(0)
H
0

1.- Your stateless bean is exposing the interface GenericCrudService (the view is what the application server registers), for that reason you use

@EJB
private GenericCrudService myEJB;

If you have multiple implementations of this interface, you can add the ejb name to the annotation for disambiguation:

@EJB(name="GenericCrudServiceBean")
private GenericCrudService myEJB;

In EJB 3.1, Session Bean without interface are exposing something called a No-Interface View. These session beans can be used only locally.

Hae answered 14/4, 2011 at 0:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.