Wednesday, 30 May 2007

Multiple persistence.xml files and spring

Today I struggled a bit with multiple persistence.xml files using Spring, JPA (duh).

I had two jar's each containing mapped domain objects and an associated persistence.xml file.

Unfortunately the JPA specification doesn't say anything about how to handle multiple persistence.xml files hence no merging is done by default.

Some searching led me to a jira issue concerning this that Juergen Hoeller have closed for spring 2.0.4.
We have just implemented our own PersistenceUnitManager capable of collecting multiple persistence.xml files and it works great!

Java code:

package com.foobar.spring;

public class MyPersistenceUnitManager extends DefaultPersistenceUnitManager {

protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
super.postProcessPersistenceUnitInfo(pui);
pui.addJarFileUrl(pui.getPersistenceUnitRootUrl());

MutablePersistenceUnitInfo oldPui = getPersistenceUnitInfo(pui.getPersistenceUnitName());
if (oldPui != null) {
List urls = oldPui.getJarFileUrls();
for (URL url : urls) {
pui.addJarFileUrl(url);
}
}
}
}
And in your spring configuration add the following (Most likely called applicationContext.xml):
<bean id="persistenceUnitManager" class="com.foobar.spring.MyPersistenceUnitManager">
<property name="persistenceXmlLocations">
<list>
<value>classpath*:META-INF/persistence.xml</value>
</list>
</property>
<property name="defaultDataSource" ref="dataSource"/>
</bean>

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

9 comments:

jayalal said...

I am getting
java.lang.IllegalArgumentException: No persistence unit with name 'XXXXX'
exception though I have jar containing XXXXX persistence unit in my applications WEB-INF/lib
Do I have to configur any other bean property?

Jacob von Eyben said...

Hmm it doesn't say much. Where are the exception thrown from? And what version of Spring are you using?
Du you have a stacktrace you could provide?

If it changes anything, I can say that in my case the persistence units in the different persistence.xml files are named the same.

Anonymous said...

Here is a solution without spring
http://forum.hibernate.org/viewtopic.php?p=2357053#2357053

Johannes said...

Thanks a lot. Without your posting i think i would be lost.
We develop several businesscomponents and got circular dependencies on our buildpath.

Now it works fine.

Johannes

Jacob von Eyben said...

@johannes: It is always nice to have feedback and good to hear that the post was useful for you.

Circular dependencies you say? That is often not a healthy thing.
You should probably see if it is possible to extract some of your code into a third (or n'th) artifact to get rid of circular dependency.
If you are using maven you have all the infrastructure to the operation.

J.C. said...

Thanks for the tip. This was great. Here's a slightly more thorough merge that will merge entity definitions from orm.xml files, properties, etc. Sorry for the formatting, it wouldn't let me use <pre&bt; or <code&bt; tags.


@Override
protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo newPU) {
super.postProcessPersistenceUnitInfo(newPU);
final URL persistenceUnitRootUrl = newPU.getPersistenceUnitRootUrl();
newPU.addJarFileUrl(persistenceUnitRootUrl);
final String persistenceUnitName = newPU.getPersistenceUnitName();
final MutablePersistenceUnitInfo oldPU = getPersistenceUnitInfo(persistenceUnitName);
if (oldPU != null) {
List<URL> urls = oldPU.getJarFileUrls();
for (URL url : urls)
newPU.addJarFileUrl(url);
List<String> managedClassNames = oldPU.getManagedClassNames();
for (String managedClassName : managedClassNames)
newPU.addManagedClassName(managedClassName);
List<String> mappingFileNames = oldPU.getMappingFileNames();
for (String mappingFileName : mappingFileNames)
newPU.addMappingFileName(mappingFileName);
Properties oldProperties = oldPU.getProperties();
Properties newProperties = newPU.getProperties();
newProperties.putAll(oldProperties);
newPU.setProperties(newProperties);
}
}

Anonymous said...

Thanks guys! J.C.'s solution is working perfectrly for multiple jar projects!

KID said...

Thanks, saved my day.

subes said...

thanks, you saved me aswell :)

maybe somebody should suggest this getting included in spring-orm as MergedPersistenceUnitManager?