spring-mvcjpawebspherejpa-2.0spring-portlet-mvc

How to use WAS standard persistence provider with Spring


I'm developing a portlet which runs in WebSphere Application Server ( - I accept the same problem to appear if it was a servlet instead of a portlet). At the moment it depends on Hibernate. As WAS provides a JPA implementation itself, which is a modified version of OpenJPA 2.0, I want to get rid of Hibernate.

This is my setup. persistence.xml:

  <?xml version="1.0" encoding="UTF-8"?>
  <persistence xmlns="http://java.sun.com/xml/ns/persistence"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
               xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
  >
      <persistence-unit name="default" transaction-type="JTA">
          <provider>org.hibernate.ejb.HibernatePersistence</provider>

          <jta-data-source>jdbc/myDb</jta-data-source>
          <properties>
              <property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform" />
              <property name="hibernate.dialect" value="org.hibernate.dialect.DB2Dialect" />
          </properties>
      </persistence-unit>
  </persistence>

myPortlet-portlet.xml

  <!-- ... -->

  <tx:jta-transaction-manager />
  <jee:jndi-lookup jndi-name="jdbc/myDb" cache="true" id="dataSource" expected-type="javax.sql.DataSource" />

  <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="persistenceUnitName" value="default" />
  </bean>

In my DAO-classes I access the entityManager by using annotations:

  @PersistenceContext(unitName = "default")
  private EntityManager entityManager;

Everything works fine using Hibernate.

According to WebSphere Application Server docs, the default persistence provider is used if you don't specify it by using the <provider/>-tag in persistence.xml. But after commenting out the provider specification, Spring throws an exception due not being able to find the provider-class:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in PortletContext resource [/WEB-INF/myPortlet-portlet.xml]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: No PersistenceProvider specified in EntityManagerFactory configuration, and chosen PersistenceUnitInfo does not specify a provider class name either

How can I use the provided JPA implementation together with Spring (Portlet) MVC?


Solution

  • Short answer

    You cannot use WebSphere's default provider by omitting the provider, if you want to use LocalContainerEntityManagerFactoryBean.

    Long answer

    Normally an entity manager is created by an entity manager factory provided by the container. You retrieve it by doing a context loopkup (EntityManager em = (EntityManager) ctx.lookup(...)) manually or use Springs jndi-lookup capability:

    <beans>
        <jee:jndi-lookup id="myEmf" jndi-name="persistence/myPersistenceUnit"/>
    </beans>
    

    In the question, a different approach is used, a LocalContainerEntityManagerFactoryBean, which creates an entity manager factory itself. This entity manager factory is a proxy that implements all the interfaces of the native entity manager factory. For creating such a proxy, Spring must know the class of the native entity manager factory. Spring uses three different ways to determine the class:

    1. Detect it by the <provider/>-entry in persistence.xml
    2. Asking a jpaVendorAdapter (specified in the equally named property of the factory bean)
    3. Using the entityManagerFactoryInterface-property of the factory bean

    And that's why you cannot completely omit the specification of your provider.