views:

45

answers:

3

I am investigating using JPA for my Data Access code. I am trying to write the business layer and data access layer so it will work in a web application and a Java SE application. Therefore I cannot use container managed persistence context. Most of my seraches on using JPA show examples in a container managed enviroment.

At the moment I get the EntityManagerFactory everytime I create a new instace of the service class. For every operation (ie add, get, etc) I open an EntityManager, start a transaction perform operations, commit and then close the EntityManager and the EntityManagerFactory. I would like the benifits of having a manged persistence context outside of the Java EE environment.

Are there any best practices when not using a container managed context? Are there any Java EE independent persistence context managers? Are there any recommended patterns?

Thanks,

Al

Update

Thank you eveyone for the info. Everything was very useful.

+4  A: 

I'm not sure about the best practices surrounding this, but I have spent a lot of time trying to make something like this work.

Basically you will need something to construct an EntityManager with. I've always used Spring for this. Their documentation has a big section on this. You can choose to use a LocalEntityManagerFactoryBean, in which case the markup would look like (from aforementioned documentation):

<bean id="myEmf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
  <property name="persistenceUnitName" value="myPersistenceUnit"/>
</bean>

This is, in principle, not encouraged, because it comes back to haunt you when you try to change data sources later on. However, I find it's very unlikely you'll run into the limitations of this for most webapps up to a certain size.

Configuration of your datasource can then be done through hibernate specific properties in your persistence unit (persistence.xml in META-INF/ directory):

<property name="hibernate.connection.driver_class" value="com.company.driver" />
<property name="hibernate.connection.url" value="some:jdbc@string" />
<property name="hibernate.connection.username" value="myuser" />
<property name="hibernate.connection.password" value="mypassword" />

To use this, if you're not using spring already, you can just grab an instance of the EntityManagerFactory from an application context and go from there (i.e. context.getBean("myEmf")).

More control is possible with LocalContainerEntityManagerFactoryBean, this one allows you to configure a data source. In principle the example from the docs should work but I found when I did this I had to specify the Hibernate persistence provider. You need a persistence.xml but it really only needs a default persistence unit and very basic configuration (perhaps identifying the dialect, e.g. if you're using hibernate with oracle 10g):

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDatasource" />
    <property name="persistenceProvider">
        <bean class="org.hibernate.ejb.HibernatePersistence" />
    </property>
</bean>

There's examples elsewhere in the spring docs on how to configure a BasicDataSource from Apache dbcp, which will give you pooling of connections as well.

As for best practices, JPA really isn't easy to work with outside of a full application server environment. There's all sorts of issues when trying to tune performance too where you'll find yourself salivating over Hibernate features that aren't available to you in JPA, and chances are your queries will end up not strictly compliant with the JPA specs anyway. If you're not using container managed, it would probably be a lot easier and saner to just use the Hibernate APIs directly.

wds
Thank you for the info. I may look into switching away from the JPA to Hibernate. I will try to use Spring as well
+1  A: 

Actually, there's a lot of support for using JPA outside of Java EE environments. See:

Both of these work inside and outside of Java EE environments.

Like you mentioned, outside of a regular Java EE enviroments, you get caught in the "open an EntityManager, start a transaction, commit the transaction, and close the EntityManager loop, which is a PITA to program and maintain.

To get the benefits of an EE environment, I recommend using the JPA part of the Spring framework. Documentation is aplenty with lots of examples. Specifically, the JpaDaoSupport class and Transactional annotation provides lots of support.

This allows you to do things like this without an EE environment:

    import java.util.List;
    import org.springframework.orm.jpa.support.JpaDaoSupport;

    public class SpeakerDAOImpl extends JpaDaoSupport implements SpeakerDAO {

    public Speaker findSpeaker(long id) {
       return getJpaTemplate().find(Speaker.class,id);
    }

    @Transactional
    public void saveSpeaker(Speaker s) {
       getJpaTemplate().persist(s);
    }
  }

Some tutorials, docs, etc.:

The Alchemist
+3  A: 

At the moment I get the EntityManagerFactory everytime I create a new instance of the service class. For every operation (ie add, get, etc) I open an EntityManager, start a transaction perform operations, commit and then close the EntityManager and the EntityManagerFactory.

You should certainly NOT open and close an EntityManagerFactory every time you create a service instance. Creating an EntityManagerFactory is a very expensive operation and should be done only once for the lifetime of the application. Just to illustrate this, here is a very basic kickoff example showing how you could handle that with a utility class:

public class JpaUtil {
    private JpaUtil() {}

    private static EntityManagerFactory emf = null;

    static {
        try {
            emf = Persistence.createEntityManagerFactory("MyPu");
        } catch (Throwable ex) {
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static EntityManager getEntityManager() {
        return emf.createEntityManager();
    }
}

But while you can make it more sophisticated (using some ThreadLocal, see this example), this won't remove the EntityManager lifecycle and transaction management code.

I would like the benefits of having a manged persistence context outside of the Java EE environment.

Then you'll need some kind of container to do that for you and Spring would be an obvious candidate and can be used for declarative transaction management and the injection of the EntityManager in Spring beans, even in a Java SE environment.

The blog post Using JPA in Spring without referencing Spring shows precisely how to do this using:

  • LocalEntityManagerFactoryBean to create the EntityManagerFactory
  • JpaTransactionManager to manage JPA transactions
  • <tx:annotation-driven /> to tell Spring to look for @Transactional
  • Your bean definition!

And provides a sample Spring configuration file that would help you to get started.

References

See also

Pascal Thivent
Agree. I also used 'Persistence.createEntityManagerFactory' to test JPA in non-container managed environment.
卢声远 Shengyuan Lu