Friday, January 26, 2007

What should replace EJB Home interface in EJB3?

In the new EJB3 standard, there is no notion of an EJB Home interface If you are making the switch from EJB2.x to EJB3, chances are you will actually come to miss this. At least, you will have to think about where to place the Home methods now.

In the old EJB2.x standard, it was actually part of the specification where to create, find and remove instances of entity beans, namely on the Home interface.

Some typical (simplified) entity code in EJB2.x would be something like:


FooHome home = jndi.lookup("...jndi path of foo bean");
Foo newInstance = home.create(42);
...
...
Foo foundInstance = home.findByPrimaryKey(....);


So, the Home interface was typically used for Creates, Reads and Deletes in the CRUD acronym. Updates where simply done by setting fields on instances.

Enforcing the developer to place create and read methods on a special EJB Home interface was actually a piece of architectural enforcement of the old specification. You had to do this, period. This is gone in EJB3. The funny thing is that the new blueprints for EJB3 does not mention this. Actually, I think the current blueprints must be a freaking joke. They seem so shallow.

I've been so fortunate to also be working a lot with spring and hibernate the last couple of years and from this, I have some advice on how to architect this in EJB3 and JPA, without the home.

In spring/hibernate the common solution at time of writing is to architect with "services beans" and "dao beans", where dao beans take the role of the old EJB Home interfaces. "Services" beans is what we commonly call "business logic". When services needs to do CRUD, they go through one or more DAOs. Taking this directly to an EJB3 example, it will go something like this:
  • For each (JPA) entity, say Foo, construct a stateless session bean FooDao
  • Annotate the SLSB with @PersistenceContext to access the data store where Foo is placed
  • Add methods for create, read and delete (the C, R and D of CRUD)
  • Create SLSBs to architect your business layer
  • Annotate the business layer with "@EJB FooDao" where data access for Foo is needed
Some example code looks like this:


public interface FooDao {
Foo create(...)
Foo findById(....);
void delete(Foo);
}

@Stateless
public class FooDaoBean implements FooDao {
@PersistenceContext
private EntityManager entityManager;

public Foo create(...) {
Foo foo = new Foo(...);
entityManager.persist(foo);
return foo;
}

public Foo findById(...) {
return entityManager.find(Foo.class, ...);
}

public void delete(Foo foo) {
entityManager.remove(foo);
}
}


And a "services" SLSB would simply do:


@Stateless
public class BarServicesBean implements BarServices {
@EJB
private FooDao fooDao;

public void doStuff(...) {
Foo foo = fooDao.findById(...);
....
}
}


Keeping strict to this strategy for all JPA mapped entities has the benefit of keeping all direct access to the EntityManager API inside DAO SLSBs while making it easy for developers to know where to look for doing CRUD on an entity. And why is it nice to keep access to the EntityManager API in one layer?

For once, it is often a good design principle to isolate access to specific APIs to certain modules or layers in your application. Another good thing about it is more directly targeted at specifics about the named queries of the JPA API contra type safety.

Looking again at our example above, the Foo entity could be defined like this:


@Entity
@NamedQuery(name="app.foo.byName", query="SELECT o FROM Foo o WHERE o.name = :name")
public class Foo {
@Id
private Long id;

@Basic(optional = false)
private String name;

....
}


To execute the named query defined above, you go through the JPA API like this:

entityManager.createNamedQuery("app.foo.byName").setParameter("name", "bar").getSingleResult()


Now what is wrong with this?
  • Setting "name" parameter is not type-safe (setParameter takes Object type as second parameter)
  • Ensuring that all parameters in the query has been set is determined at runtime
If we later on redefine the named query to, say, take an extra parameter, we would need to find all places it was used and add setting of the parameter. But we can do better - by encapsulating access to the finder in one method, placed in a dao bean, like this:


public Foo findByName(String nameToLookFor) {
return entityManager.createNamedQuery("app.foo.byName").setParameter("name", nameToLookFor).getSingleResult();
}


All services (or other daos) that need to access the finder shall go through this method. If we add a new parameter to the finder, we will also add a new parameter to the findByName method - a thing that is statically checked at compile time. Much better.

To sum up, what I'm saying is, when switching from EJB2.x to EJB3:
  • Think about the Home interface being gone
  • Go to lengths to avoid having @PersistenceContext shattered all over your business logic
  • One architectural advice is the dao stuff above which is proven to work well with spring and hibernate

2 comments:

yagmurunsesi said...

thanks
renovationdoctors.com
turizmseyahat.blogspot.com
www.yagmurunsesi.org
yagmurunsesiorg.blogspot.com
turkuntarihi.blogspot.com
websitesiyapamak.blogspot.com
saglik-k.blogspot.com
ders-hane.blogspot.com

Shyam Prasad said...

Nothing wrong in removing home interface. Earlier version of ejb does not require a seperate bean to persist entities.
Due to the advent of jpa, the intelligence to save itself is moved out of the business object. So now, it is mandatory to have a session, bean/DAO etc to save an entity. So, whatever session bean we are using to save the entity, we can use it as home interface replacement.