9.5. Using Java Persistence in EJB components

A managed runtime environment implies some sort of container. Your application components live inside this container. Most containers these days are implemented using an interception technique, method calls on objects are intercepted and any code that needs to be executed before (or after) the method is applied. This is perfect for any cross-cutting concerns: Opening and closing an EntityManager, because you need it inside the method that is called, is certainly one. Your business logic doesn't need to be concerned with this aspect. Transaction demarcation is another concern a container can take care of for you. (You'll likely find other aspects in any application.)

Unlike older application servers from the EJB 2.x era, containers that support EJB 3.0 and other Java EE 5.0 services are easy to install and use—refer to our discussion in chapter 2, section 2.2.3, "Introducing EJB components," to prepare your system for the following section. Furthermore, the EJB 3.0 programming model is based on plain Java classes. You shouldn't be surprised if you see us writing many EJBs in this book; most of the time, the only difference from a plain JavaBean is a simple annotation, a declaration that you wish to use a service provided by the environment the component will run in. If you can't modify the source code and add an annotation, you can turn a class into an EJB with an XML deployment descriptor. Hence, (almost) every class can be a managed component in EJB 3.0, which makes it much easier for you to benefit from Java EE 5.0 services.

The entity classes you've created so far aren't enough to write an application. You also want stateless or stateful session beans, components that you can use to encapsulate your application logic. Inside these components, you need the services of the container: for example, you usually want the container to inject an EntityManager, so that you can load and store entity instances.

9.5.1. Injecting an EntityManager

Remember how you create an instance of an EntityManager in Java SE? You have to open it from an EntityManagerFactory and close it manually. You also have to begin and end a resource-local transaction with the EntityTransaction interface.

In an EJB 3.0 server, a container-managed EntityManager is available through dependency injection. Consider the following EJB session bean that implements a particular action in the CaveatEmptor application:

@Stateless
public class ManageAuctionBean implements ManageAuction {

    // Use field injection:

    @PersistenceContext
    private EntityManager em;

    // or setter injection:
    //
    // @PersistenceContext
    // public void setEntityManager(EntityManager em) {
    //    this.em = em;
    // }

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public Item findAuctionByName(String name) {
       return (Item) em.createQuery()...
       ...

    }

}

It's a stateless action, and it implements the ManageAuction interface. These details of stateless EJBs aren't our concern at this time, what is interesting is that you can access the EntityManager in the findAuctionByName() method of the action. The container automatically injects an instance of an EntityManager into the em field of the bean, before the action method executes. The visibility of the field isn't important for the container, but you need to apply the @PersistenceContext annotation to indicate that you want the container's service. You could also create a public setter method for this field and apply the annotation on this method. This is the recommended approach if you also plan to set the EntityManager manually—for example, during integration or functional testing.

The injected EntityManager is maintained by the container. You don't have to flush or close it, nor do you have to start and end a transaction—in the previous example you tell the container that the findAuctionByName() method of the session bean requires a transaction. (This is the default for all EJB session bean methods.) A transaction must be active when the method is called by a client (or a new transaction is started automatically). When the method returns, the transaction either continues or is committed, depending on whether it was started for this method.

The persistence context of the injected container-managed EntityManager is bound to the scope of the transaction, Hence, it's flushed automatically and closed when the transaction ends. This is an important difference, if you compare it with earlier examples that showed JPA in Java SE! The persistence context there wasn't scoped to the transaction but to the EntityManager instance you closed explicitly. The transaction-scoped persistence context is the natural default for a stateless bean, as you'll see when you focus on conversation implementation and transactions later, in the following chapters.

A nice trick that obviously works only with JBoss EJB 3.0 is the automatic injection of a Session object, instead of an EntityManager:

@Stateless
public class ManageAuctionBean implements ManageAuction {

    @PersistenceContext
    private Session session;
    ...

}

This is mostly useful if you have a managed component that would rely on the Hibernate API.

Here is a variation that works with two databases—that is, two persistence units:

@Stateless
public class ManageAuctionBean implements ManageAuction {

    @PersistenceContext(unitName = "auctionDB")
    private EntityManager auctionEM;

    @PersistenceContext(unitName = "auditDB")
    private EntityManager auditEM;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void createAuction(String name, BigDecimal price) {
        Item newItem = new Item(name, price);
        auctionEM.persist(newItem);
        auditEM.persist( new CreateAuctionEvent(newItem) );
        ...

    }

}

The unitName refers to the configured and deployed persistence unit. If you work with one database (one EntityManagerFactory or one SessionFactory), you don't need to declare the name of the persistence unit for injection. Note that EntityManager instances from two different persistence units aren't sharing the same persistence context. Naturally, both are independent caches of managed entity objects, but that doesn't mean they can't participate in the same system transaction.

If you write EJBs with Java Persistence, the choice is clear: You want the EntityManager with the right persistence context injected into your managed components by the container. An alternative you'll rarely use is the lookup of a container-managed EntityManager.

9.5.2. Looking up an EntityManager

Instead of letting the container inject an EntityManager on your field or setter method, you can look it up from JNDI when you need it:

@Stateless
@PersistenceContext(name = "em/auction", unitName = "auctionDB")
public class ManageAuctionBean implements ManageAuction {

    @Resource
    SessionContext ctx;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public Item findAuctionByName(String name) {
        EntityManager em = (EntityManager) ctx.lookup("em/auction");

        return (Item) em.createQuery()...
    }

}

Several things are happening in this code snippet: First, you declare that you want the component environment of the bean populated with an EntityManager and that the name of the bound reference is supposed to be em/auction. The full name in JNDI is java:comp/env/em/auction—the java:comp/env/ part is the so called bean-naming context. Everything in that subcontext of JNDI is bean-dependent. In other words, the EJB container reads this annotation and knows that it has to bind an EntityManager for this bean only, at runtime when the bean executes, under the namespace in JNDI that is reserved for this bean.

You look up the EntityManager in your bean implementation with the help of the SessionContext. The benefit of this context is that it automatically prefixes the name you're looking for with java:comp/env/; hence, it tries to find the reference in the bean's naming context, and not the global JNDI namespace. The @Resource annotation instructs the EJB container to inject the SessionContext for you.

A persistence context is created by the container when the first method on the EntityManager is called, and it's flushed and closed when the transaction ends—when the method returns.

Injection and lookup are also available if you need an EntityManagerFactory.

9.5.3. Accessing an EntityManagerFactory

An EJB container also allows you to access an EntityManagerFactory for a persistence unit directly. Without a managed environment, you have to create the EntityManagerFactory with the help of the Persistence bootstrap class. In a container, you can again utilize automatic dependency injection to get an EntityManagerFactory:

@Stateless
public class ManageAuctionBean implements ManageAuction {

    @PersistenceUnit(unitName = "auctionDB")
    EntityManagerFactory auctionDB;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public Item findAuctionByName(String name) {
        EntityManager em = auctionDB.createEntityManager();
        ...
        Item item = (Item) em.createQuery()...
        ...
        em.flush();
        em.close();
        return item;
    }

}

The unitName attribute is optional and only required if you have more than one configured persistence unit (several databases). The EntityManager you created from the injected factory is again application-managed—the container won't flush this persistence context, nor close it. It's rare that you mix container-managed factories with application-managed EntityManager instances, but doing so is useful if you need more control over the lifecycle of an EntityManager in an EJB component.

You may create an EntityManager outside of any JTA transaction boundaries; for example, in an EJB method that doesn't require a transaction context. It's then your responsibility to notify the EntityManager that a JTA transaction is active, when needed, with the joinTransaction() method. Note that this operation doesn't bind or scope the persistence context to the JTA transaction; it's only a hint that switches the EntityManager to transactional behavior internally.

The previous statements aren't complete: If you close() the EntityManager, it doesn't immediately close its persistence context, if this persistence context has been associated with a transaction. The persistence context is closed when the transaction completes. However, any call of the closed EntityManager throws an exception (except for the getTransaction() method in Java SE and the isOpen() method). You can switch this behavior with the hibernate. ejb.discard_ pc_on_close configuration setting. You don't have to worry about this if you never call the EntityManager outside of transaction boundaries.

Another reason for accessing your EntityManagerFactory may be that you want to access a particular vendor extension on this interface, like we discussed in chapter 2, section 2.2.4, "Switching to Hibernate interfaces."

You can also look up an EntityManagerFactory if you bind it to the EJB's naming context first:

@Stateless
@PersistenceUnit(name= "emf/auction", unitName = "auctionDB")
public class ManageAuctionBean implements ManageAuction {

    @Resource
    SessionContext ctx;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public Item findAuctionByName(String name) {
        EntityManagerFactory auctionDB =
            (EntityManagerFactory) ctx.lookup("emf/auction");

        EntityManager em = auctionDB.createEntityManager();
        ...
        Item item = (Item) em.createQuery()...
        ...

        em.flush();
        em.close();
        return item;
    }

}

Again, there is no particular advantage if you compare the lookup technique with automatic injection.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.19.211.164