Tuesday, January 30, 2007

Using Hibernate as a Pluggable EJB 3 JPA Provider

EJB 3 supports pluggable persistence contract that allows you to plug-in any persistence provider implementing JPA 1.0 with a container that supports JavaEE 5 / EJB 3 spec. It’s provides choice to users to choose their JPA provider of choice. I thought I would give a spin and check whether this plug-ability story really works and here is the result!


I used Oracle Application Server 10g 10.1.3.1 that has support for EJB 3.0 spec (not yet EJB 3 / Java EE 5 compliant) and Hibernate 3.2 that is JPA 1.0 compliant to see whether the plug ability story is real! Oracle Application Server by default uses TopLink Essentials as the JPA provider. Note that TopLink Essentials is the Reference Implementation for the JPA spec.

Loading the Hibernate Libraries in the server

In order to use Hibernate as a pluggable persistence provider, you have to make sure that Hibernate Entity Manager and related libraries are made available to the container class path.

I created a shared library in OC4J named Hibernate and uploaded the required jars. Following is the definition of the shared library:

<shared-library name="Hibernate" version="3.2">
<code-source path="hibernate-entitymanager.jar"/>
<code-source path="hibernate3.jar"/>
<code-source path="jboss-archive-browsing.jar"/>
<code-source path="dom4j-1.6.1.jar"/>
<code-source path="hibernate-annotations.jar"/>
<code-source path="javassist.jar"/>
<code-source path="commons-collections-2.1.1.jar"/>
<code-source path="ehcache-1.2.3.jar"/>
<code-source path="c3p0-0.9.1.jar"/>
<code-source path="concurrent-1.3.2.jar"/>
<code-source path="cglib-2.1.3.jar"/>
<code-source path="asm.jar"/>
<code-source path="asm-attrs.jar"/>
<code-source path="antlr-2.7.6.jar"/>
<code-source path="commons-logging-1.0.4.jar"/>
<code-source path="log4j-1.2.11.jar"/>
</shared-library>

Make sure that application imports the shared library. You can do so by either importing the library during deployment or packaging an orion-application.xml as follows:

<orion-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://xmlns.oracle.com/oracleas/schema/orion-application-10_0.xsd" deployment-version="10.1.3.1.0" default-data-source="jdbc/OracleDS" component-classification="external"
schema-major-version="10" schema-minor-version="0" >
<imported-shared-libraries>
<import-shared-library name="Hibernate"/>
</imported-shared-libraries>
</orion-application>

Configuring the Persistence Unit

You need to configure the persistence unit to specify the persistence provider and other vendor specific properties in the persistence.xml. For my test application I created a simple entity class and packaged the following persistence.xml in my application that uses Hibernate 3.2 as the JPA provider.

<persistence>
<persistence-unit name="howto">
<jta-data-source>jdbc/OracleDS</jta-data-source>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.OC4JTransactionManagerLookup"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.OracleDialect" />
<property name="hibernate.hbm2ddl.auto" value="create-drop"/>
<property name="hibernate.show_sql" value="true" />

</properties>
</persistence-unit>
</persistence>

Note that in above I set provider class to org.hibernate.ejb.HibernatePersistence that tells the container to use Hibernate 3.2 as the persistence provider instead of using TopLink Essentials as the JPA provider.

Access to Transaction Manager

The primary reason behind the pluggable persistence provider is to provide facility to use JPA with a container managed Entity Manager and hence Hibernate needs to access the JTA Transaction Manager of the container.


Hibernate 3.2 does not use the JTA TransactionSynchronizationRegistry whose sole purpose is for such integration.

Instead it provides classes for each application server that looks up the Transaction Manager.
For example,org.hibernate.transaction.OC4JtransactionManagerLookup is provided with Hibernate as a persistence property to lookup OC4J’s transaction manager as follows:

<property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.OC4JTransactionManagerLookup"/>

If you want to use another application server then you have to the class for that server.


Deploy and Run Your Application

You can package your entities and then deploy to the server

Now after you deploy your application into OC4J you should be able to run your application. If you used the property hibernate.show_sql to true, you will see similar output from Hibernate in your console.

07/01/30 12:11:54 Hibernate: insert into EMP (ename, sal, EMPNO) values (?, ?, ?
)
07/01/30 12:11:55 Hibernate: select employee0_.EMPNO as EMPNO0_0_, employee0_.en
ame as ename0_0_, employee0_.sal as sal0_0_ from EMP employee0_ where employee0_

Conclusion

I did some testing and the application works as expected and hence I conclude EJB 3 delivers on the plug-ability story and Oracle Application Server 10g is Hot Pluggable!

16 comments:

Anonymous said...

As I've made some good experiences using Hibernate 3.0 and I've tested 3.2 using JBoss I was very interessted to get an idea how the server that our company uses (OC4J) can deal with this JPA - implementation. I have to say: Everything went cool after reading this article!
Wolfram (wolfram.welschinger@web.de)

Anonymous said...

I tried the same with standalone OC4J and am having issues.
The issues stem from the vicious cycle of dependencies that I belive come up due to obscure parts of code in the hibernate libraries. I am able to compile and build alright. I have tried to deploy with -Xverify:none option and it didnt help. I still get errors continually about missing classes whenever I try to deploying.

Anonymous said...

I changed the shared library definition to get rid of all class not found exceptions during deployment.

Here my definition:

shared-library name="Hibernate" version="3.2">
code-source path="ant-1.6.5.jar"/>
code-source path="ant-antlr-1.6.5.jar"/>
code-source path="ant-junit-1.6.5.jar"/>
code-source path="ant-launcher-1.6.5.jar"/>
code-source path="ant-swing-1.6.5.jar"/>
code-source path="antlr-2.7.6.jar"/>
code-source path="asm-attrs.jar"/>
code-source path="asm.jar"/>
code-source path="c3p0-0.9.1.jar"/>
code-source path="cglib-2.1.3.jar"/>
code-source path="checkstyle-all.jar"/>
code-source path="cleanimports.jar"/>
code-source path="commons-collections-2.1.1.jar"/>
code-source path="commons-logging-1.0.4.jar"/>
code-source path="concurrent-1.3.2.jar"/>
code-source path="connector.jar"/>
code-source path="dom4j-1.6.1.jar"/>
code-source path="ehcache-1.2.3.jar"/>
code-source path="ejb3-persistence.jar"/>
code-source path="hibernate-annotations.jar"/>
code-source path="hibernate-commons-annotations.jar"/>
code-source path="hibernate-entitymanager.jar"/>
code-source path="hibernate-validator.jar"/>
code-source path="hibernate3.jar"/>
code-source path="jaas.jar"/>
code-source path="jacc-1_0-fr.jar"/>
code-source path="javassist.jar"/>
code-source path="jaxen-1.1-beta-7.jar"/>
code-source path="jboss-archive-browsing.jar"/>
code-source path="jboss-cache.jar"/>
code-source path="jboss-common.jar"/>
code-source path="jboss-jmx.jar"/>
code-source path="jboss-system.jar"/>
code-source path="jdbc2_0-stdext.jar"/>
code-source path="jgroups-2.2.8.jar"/>
code-source path="jta.jar"/>
code-source path="junit-3.8.1.jar"/>
code-source path="log4j-1.2.11.jar"/>
code-source path="oscache-2.1.jar"/>
code-source path="proxool-0.8.3.jar"/>
code-source path="swarmcache-1.0rc2.jar"/>
code-source path="syndiag2.jar"/>
code-source path="versioncheck.jar"/>
code-source path="xerces-2.6.2.jar"/>
code-source path="xml-apis.jar"/>
/shared-library>

But it does not work at all for me. I get the following exception when deploying:

Caused by: java.lang.RuntimeException: error trying to scan jar-file>: file:/C:
/paul/tools/oc4j/10.1.3.2.0/j2ee/home/applications/App/lib/test-jar.
jar
at org.hibernate.ejb.Ejb3Configuration.scanForClasses(Ejb3Configuration.
java:631)
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:
347)
at org.hibernate.ejb.HibernatePersistence.createContainerEntityManagerFa
ctory(HibernatePersistence.java:126)
at com.evermind.server.ejb.persistence.PersistenceUnitManagerImpl.create
ContainerEntityManagerFactory(PersistenceUnitManagerImpl.java:192)
... 18 more
Caused by: java.lang.RuntimeException: java.util.zip.ZipException: Das System ka
nn die angegebene Datei nicht finden
at org.jboss.util.file.JarArchiveBrowser. init>(JarArchiveBrowser.java:7
4)
at org.jboss.util.file.ArchiveBrowser.getBrowser(ArchiveBrowser.java:64)

at org.hibernate.ejb.Ejb3Configuration.scanForClasses(Ejb3Configuration.
java:622)
... 21 more
Caused by: java.util.zip.ZipException: Das System kann die angegebene Datei nich
t finden
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile. init>(ZipFile.java:203)
at java.util.jar.JarFile. init>(JarFile.java:132)
at java.util.jar.JarFile. init>(JarFile.java:97)
at org.jboss.util.file.JarArchiveBrowser. init>(JarArchiveBrowser.java:6
9)

Any idea?

Anonymous said...

I am also having problems, can't I just not use any shared libraries and just bundle everything in my WAR lib folder and go from there?
My error is 07/04/23 11:48:57 SEVERE: ProgressObjectImpl.reportError [TestJPA:TestJPA] - Exc
eption creating EntityManagerFactory using PersistenceProvider class org.hiberna
te.ejb.HibernatePersistence for persistence unit TestPU.oracle.oc4j.admin.jmx.sh
ared.exceptions.InternalException: [TestJPA:TestJPA] - Exception creating Entity
ManagerFactory using PersistenceProvider class org.hibernate.ejb.HibernatePersis
tence for persistence unit TestPU.
at oracle.oc4j.admin.jmx.shared.deploy.NotificationUserData.(Notif
icationUserData.java:107)
at oracle.oc4j.admin.internal.Notifier.reportError(Notifier.java:429)
at oracle.oc4j.admin.internal.DeployerBase.execute(DeployerBase.java:123
)
at oracle.oc4j.admin.jmx.server.mbeans.deploy.OC4JDeployerRunnable.doRun
(OC4JDeployerRunnable.java:52)
at oracle.oc4j.admin.jmx.server.mbeans.deploy.DeployerRunnable.run(Deplo
yerRunnable.java:81)

Anonymous said...

Yo tengo el mismo error del deploy
con el jdeveloper se funciona pero al momento de realizar el deployer manda error de

Exception creating EntityManagerFactory using PersistenceProvider class org.hiberna
te.ejb.HibernatePersistence for persistence unit
como corrijo ese error??

janez said...

Everything went cool after reading this article!
Not for me. I still get this annoying error Exception creating EntityManagerFactory using PersistenceProvider class org.hibernate.ejb.HibernatePersistence for persistence

bjs o^o said...

Very helpful article - thanks!

Janez - try this:

In file j2ee/home/config/server.xml, remove the following entry:

<code-source path="../../../toplink/jlib/toplink-essentials.jar"/>.

Also, in file orion-application.xml, add the 'remove-inherited' attribute:

<imported-shared-libraries>
<import-shared-library name="Hibernate"/>
<remove-inherited name="oracle.toplink"/>
</imported-shared-libraries>

This worked for us.

Daniel Cruz said...

I like this blog is fantastic, is really good written. Congratulation. Do you want to see something more? Read it...: Costa Rica is a country with a extremely sense of freedom. The landscapes are for much the most green in whole center america.The chances of investement are way to high, the average of Americans, European and people of the entire planet who is buying here is up in the sky !!!
Great investment opportunity in Costa Rica: condos, costa rica real estate, costa rica property. Visit us for more info at: http://www.jaco-bay.com/

Anonymous said...

Same problem - it's not caring that I'm removing TopLink. Even on the OC4J Admin console it shows that oracle.toplink is not included in the class-loading libraries. Yet it stil ll tries to load the TopLink persistence provider.

I have an open Oracle SA about this issue; which may only affect OC4J 10.1.3.3 (the latest, May 2008) release.

Oracle sucks, btw - good thing they bought BEA. Thanks for your useful blog.

Paul said...

I didn't need to define the hibernate shared library. I just specified the hibernate.transaction.manager_lookup_class in persistence.xml and removed the imported shared libraries in orion-application.xml. (as specified in the original post)

Then deployment went fine.

Satpal said...

Saltmarch Media is organizing Great Indian Developer Summit event in Bangalore. This Summit will be a boost for the Software Developing Industries. It covers the topics like .Net, Java, EJB, JPA, Hibernate and Richweb and has 1 day workshop at the end as well. Any one attending this event?

Register @ www.developersummit.com

Michael said...

We were getting Exception creating EntityManagerFactory errors and found two date-related issues to avoid them.

1. Do not use Java fields of type java.sql.Date! Use java.util.Date or java.util.Calendar instead.

2. If your Oracle column is type DATE and you specify the annotation @Temporal(TemporalType.TIMESTAMP) so the time component is not truncated, Hibernate will fail validation if you put <property name="hibernate.hbm2ddl.auto" value="validate"/> in persistence.xml. If you remove that line from persistence.xml it will be OK.

Viagra Online said...

That Oracle application was the same solution that I took for a problem similar to this. However, I uses another kind of code. Buy Viagra Viagra

cialis tadalafil said...

hey buddy,this is one of the best posts that I�ve ever seen; you may include some more ideas in the same theme. I�m still waiting for some interesting thoughts from your side in your next post.

Cialis said...

Great tutorial!

Elliott Broidy said...

Great info