Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Although the Seam development team encourages you to use Hibernate as the JPA provider, Seam is capable of working with any JPA provider. Hibernate is recommended for a good reason. It provides several vendor extensions that Seam is able to leverage to your advantage. If you are willing to pass on these enhancements (most notably manual flushing and Hibernate Search), then you should have no problem swapping out Hibernate for another provider.

Changing the JPA persistence provider

There are three steps to changing the JPA persistence provider in a Seam application:

  1. Make sure the JPA provider is deployed to the application server (the JARs are available on the classpath)
  2. Declare the SPI class in persistence.xml
  3. Tell Seam to use the generic JPA persistence provider manager component (instead of Hibernate)

After you have the JPA provider configured, you should check your code for uses of Hibernate-specific extensions. This will keep you from bumping into exceptions.

Step 1: Preparing an alternate provider

The first step is usually just deciding to use the JPA provider that comes with the application server. For instance, if you are using GlassFish V2, TopLink Essentials is the bundled provider. Otherwise, you have to add the JAR files to the application server's extension directory. You had to do this same step if you have ever used Hibernate on GlassFish.

Step 2: Attaching the persistence unit to the provider

The second step is how you tell JPA which provider you want to use. You enter the SPI class that extends the javax.persistence.spi.PersistenceProvider interface in the <provider> element under <persistence-unit> in the persistence unit descriptor (persistence.xml).

<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" 
   version="1.0">
   <persistence-unit name="pu">
      <provider>oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider</provider>
      ...
   </persistence-unit>
</persistence>

If there is only one provider on the classpath, JPA will automatically detect it and use it. It's when you have more than one JPA provider on the classpath that you have to be explicit.

You may need to specify other settings in persistence.xml that are specific to the provider, just like Hibernate has its own set of properties. For instance, with TopLink, you need to add a property that sets the target database:

<property name="toplink.target-database" value="MySQL4"/>

I also find that when I am using an exploded archive, I have to tell TopLink not to exclude unlisted classes. That's done by adding the following element above the <properties> element:

<exclude-unlisted-classes>false</exclude-unlisted-classes>

Your mileage may vary. I'll admit that depending on your packaging, getting the persistence unit setup right can be hairy. This has nothing to do with Seam.

Step 3: Breaking the news to Seam that you won't be using Hibernate

The third and final step to configuring the application is to give Seam the hint as to which persistence provider manager component to use. That's how Seam keeps straight which JPA providers support which features. For any non-Hibernate JPA provider, you define the generic JPA persistence provider in the Seam component descriptor (components.xml).

<components xmlns="http://jboss.com/products/seam/components"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   ...
   xsi:schemaLocation="
      ...
      http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd">

   ...
   <component name="org.jboss.seam.persistence.persistenceProvider"
      class="org.jboss.seam.persistence.PersistenceProvider"/>
</components>

Again, this is only necessary if Hibernate is on the classpath, since Seam assumes you want to use Hibernate in that case (see JBSEAM-2785 for plans to fix this feature).

However, you will run into problems if you are using Seam < 2.1.2. In older versions, Seam would throw an exception when it attempted to switch the persistence contexts to manual flush mode prior to rendering (JBSEAM-3030), a flush mode that is only available in Hibernate. This has now been fixed. Until you have a chance to upgrade, you need to instead add the following component to your application to suppress this exception:

package com.domain.app.persistence;

import javax.persistence.EntityManager;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.persistence.PersistenceProvider;

@Name("org.jboss.seam.persistence.persistenceProvider")
public class NoManualFlushPersistenceProvider extends PersistenceProvider {

   @Override
   public void setFlushModeManual(EntityManager entityManager) {
      // no-op
   }
}

You should now be able to start your Seam application using the alternate JPA provider! The last step is to make sure you reset your expectations.

Implications

First and foremost, without Hibernate, manual flushing will not be available. Therefore, when using a long-running conversation, you are likely to get flushing of the persistence context after each request. The reason for this is that Seam uses a wrapper (global) transaction around each non-faces request and two transactions for each faces request. Since a JPA provider will flush prior to the transaction committing, you are guaranteed the flush will happen on every request. You can choose to disable Seam's wrapper transactions by configuring the <core:init> component in the Seam component descriptor as follows:

<core:init transaction-management-enabled="false"/>

Now you can control flushing by avoiding any calls to transactional methods until you are ready to send dirty changes to the database. That's the JPA way, so to speak.

Also be aware that you cannot cast EntityManager to the FullTextEntityManager (Hibernate Search). Also, when you retrieve the delegate from the EntityManager, it will be the delegate for the provider, which is a oracle.toplink.essentials.ejb.cmp3.EntityManager in the case of TopLink.

There you have it, Seam with an alternate JPA provider!