1

I would like to equip my EJBs with CRUD methods.

I have many entities and multiple persistence units.

I want to implement my CRUD methods once and invoke them on different persistence units.

I tried to achieve this using inheritance, but it isn't working.

The CRUD class is:

public class FormEBean<T> {

    protected EntityManager em;

    public EntityManager getEm() {
        return em;
    }

    public void setEm(EntityManager em) {
        this.em = em;
    }

    public String create(T entity) {
        try {
            em.persist(entity);
            em.flush();
            return null;
        } catch (Exception ex) {
            return ex.getLocalizedMessage();
        }
    }

    public void create(List<T> entityList) {
        for (T entity : entityList) {
            em.persist(entity);
        }
    }

    public void edit(T entity) {
        em.merge(entity);
    }

    public void edit(Set<T> entitySet) {
        Iterator<T> it = entitySet.iterator();
        while (it.hasNext()) {
            T entity = it.next();
            em.merge(entity);
        }
    }

    public void remove(T entity) {
        em.remove(em.merge(entity));
    }

    public void remove(T[] listaDaRimuovere) {
        for (T entity : listaDaRimuovere) {
            em.remove(em.merge(entity));
        }
    }

    public void remove(List<T> listaDaRimuovere) {
        for (T entity : listaDaRimuovere) {
            em.remove(em.merge(entity));
        }
    }
}

So I tryed in this way:

@Stateless
@Remote(MyEBeanRemote.class)
public class MyEBean extends FormEBean<MyEntityClass> implements MyEBeanRemote {

    @PersistenceContext(unitName = "siat-ejbPU")
    private EntityManager em;

    // code here
}

Even if I haven't got any error, the CRUD functions have no effect on my DataBase.

Instead if I insert them directly into MyEBean it behaves as expected.

I don't want to use @PersistenceContext(unitName = "siat-ejbPU") in the FormEBean, because EJBs could use different persistence units.

Is there a way to solve this problem?

Is there a pattern I can use to reuse my code?

Edit:

The aim of this question is finding a solution that maximizes CRUD code reuse in EJBs belonging to different EJB modules and having different persistence units.

Using generic methods in a stateless session bean seems like a good solution, but only for reusing CRUD code for EJBs in the same persistence unit.

What solution could be persistence unit independent (if it exists)?

jahroy
  • 22,322
  • 9
  • 59
  • 108
maxqua72
  • 391
  • 1
  • 9
  • 25

3 Answers3

3

Hi you can use setter method:

@Stateless
@Remote(MyEBean.class)
public class MyEBean extends FormEBean implements MyEBeanRemote {

    final Logger logger = LoggerFactory.getLogger(MyEBean.class);

    @PersistenceContext(unitName = "siat-ejbPU")
    @Override
    public void setEmCrud(EntityManager em) {
        super.setEmCrud(em)
    }

Worked for me.

Xavi López
  • 27,550
  • 11
  • 97
  • 161
0

You need to make your CRUD methods generic (create/edit/remove).

The class named FormEBean should NOT be generic.

If you make the methods generic in stead of the class, you can implement them once and use them with any entity class. Generic crud methods might look something like this:

public <T> T create(T someEntity) {
    em.persist(someEntity);
    return someEntity;
}

public <T> void create(Collection<T> entities) {
    for (T entity : entities) {
        em.persist(entity);
    }
}

public <T> void edit(T entity) {
    em.merge(entity);
}

public <T> void edit(Collection<T> entities) {
    for (T currentEntity : entities) {
        em.merge(currentEntity);
    }
}

Put those in your session bean and use them anywhere to operate on any entity.

/**
 *  Example managed bean that uses our
 *  stateless session bean's generic CRUD
 *  methods.
 *
 */

class ExampleManagedBean {

    @EJB
    MyCrudBeanLocal crudBean;

    public void createStuff() {
        // create two test objects
        Customer cust   =  createRandomCustomer();
        FunkyItem item  =  createRandomItem();
        // use generic method to persist them
        crudBean.create(cust);
        crudBean.create(item);
    }
}

This answer does exactly what I describe and provides example code:

Another example:

Community
  • 1
  • 1
jahroy
  • 22,322
  • 9
  • 59
  • 108
  • Thanks, ok to use generic methods instead of generic class but if I understand correctly: if I use an interface, I will have to implement the generic methods for all the ejBeans (without saving code); Could you elaborate your point about one single class handling CRUD operations? How can I use it in the ejBeans? By inheritance? Anyway how to handle entityManager? – maxqua72 Aug 25 '12 at 08:43
  • @maxqua72 - You would simply implement a single _Stateless Session Bean_ with generic crud methods. You would use this session bean to handle database operations for all of your entities. Check out the first link above. It literally does exactly what I'm suggesting and provides code examples. – jahroy Aug 25 '12 at 18:15
  • OK, I understand now and it is a perfect solution when using one persistence unit only. When I have many EJB modules and each one has its own persistence unit, I should implement a class with generic methods for each ejb module (for each persistence unit), so for instance 10 EJB modules means repeating 10 times the same code with the same generic methods. Is there no way for a persistence unit indipendent solution (that is what I meant in my original question)? – maxqua72 Aug 25 '12 at 22:40
0

I found a solution that solves the problem.

It's based on jahroy's answer, but uses inheritance to deal with multiple persitence units.

The common code is a base class (not generic but with generic methods):

    public class FormEBean {

        final Logger logger = LoggerFactory.getLogger(FormEBean.class);

        protected EntityManager emCrud;

        public EntityManager getEmCrud() {
            return emCrud;
        }

        public void setEmCrud(EntityManager em) {
            emCrud = em;
        }

        public <T> String create(T entity) {
            String exception = null;
            try {
                emCrud.persist(entity);
                emCrud.flush();
            } catch (Exception ex) {
                //ex.printStackTrace();
                exception = ex.getLocalizedMessage();
            }
            return exception;
        }

        public <T> void create(List<T> entityList) {
            for (T entity : entityList) {
                emCrud.persist(entity);
            }
        }

        public <T> void edit(T entity) {
            emCrud.merge(entity);
        }

        public <T> void edit(Set<T> entitySet) {
            Iterator<T> it = entitySet.iterator();
            while (it.hasNext()) {
                T entity = it.next();
                emCrud.merge(entity);
                emCrud.flush();
            }
        }

        public <T> void remove(T entity) {
            emCrud.remove(emCrud.merge(entity));
        }

        public <T> void remove(T[] listaDaRimuovere) {
            for (T entity : listaDaRimuovere) {
                emCrud.remove(emCrud.merge(entity));
            }
        }

        public <T> void remove(List<T> listaDaRimuovere) {
            for (T entity : listaDaRimuovere) {
                emCrud.remove(emCrud.merge(entity));
            }
        }
     }

... and this is the interface:

public interface FormEBeanRemote {
    public void setEmCrud(EntityManager em);
    public <T> String create(T entity);
    public <T> void create(List<T> entityList);
    public <T> void edit(T entity);
    public <T> void edit(Set<T> entitySet);
    public <T> void remove(T entity);
    public <T> void remove(T[] listaDaRimuovere);
    public <T> void remove(List<T> listaDaRimuovere);
}

The EJB (stateless session bean) looks like this:

@Stateless
@Remote(MyEBean.class)
public class MyEBean extends FormEBean implements MyEBeanRemote {

    final Logger logger = LoggerFactory.getLogger(MyEBean.class);

    @PersistenceContext(unitName = "siat-ejbPU")
    private EntityManager em;

    public EntityManager getEm() {
        return em;
    }

    public void setEm(EntityManager em) {
        this.em = em;
    }

    @PostConstruct
    public void postConstruct() {
        this.setEmCrud(em);
    }

...where

@Remote
public interface MyEBeanRemote extends FormEBeanRemote {
......
}

Note that the EJB uses the postConstruct method to set the entityManager, which is delegated to perform CRUD operations on a specific persistence unit.

It's working like a charm so far.

If someone finds any pitfalls please let me know.

jahroy
  • 22,322
  • 9
  • 59
  • 108
maxqua72
  • 391
  • 1
  • 9
  • 25
  • This looks pretty good. It should allow you to write your CRUD methods once and have one _stateless session bean_ for each persistence unit. Here's a couple thoughts/suggestions: 1.) You might want to make _FormEBean_ abstract. It doesn't look like it's meant to be instantiated. 2.) I would replace _Set_ and _List_ with _Collection_ in your CRUD methods. Both _List_ and _Set_ implement _Collection_, which allows them to be treated the same here. – jahroy Aug 29 '12 at 20:29