Configuring modular JPA applications with Spring

One of the top problem with JPA configuration is the inability to provide a true modular configuration of an application’s persistence unit. The persistence unit declares the entities that are to be included for a particular JPA-compliant implementation like Hibernate.

The problem in practice is that it is forbidden to link entities if they are placed in two different persistence units so a single persistence unit, defined at the application level must be used. This single configuration file needs to declare the entities and/or the list of JAR files to be scanned in the application and it needs to be aware of all the modules that are used by the application (that is, it cannot update itself based on the available modules).

We ended up doing something like this:

where the sf.persistence.jars.location was a token replaced by our build tool according to the actual location of these modules. All in all, the JPA spec does not support a use case where the persistence unit of the application would build itself based on the modules available in the application. Users tried to find a work around using Spring (see the Spring forum and SPR-2598).

The sample project attached demonstrates how we can work around JPA’s limitation in that area. It provides an extension of Spring’s DefaultPersistenceUnitManager that is able to merge multiple persistence units in a single one based either on the name of the persistence unit or a prefix. The implementation takes two parameters: the persistence unit name and whether the manager is strict or not. If it is strict, it only merges persistence units having the exact same name as the provided one. If it’s not strict, it takes any persistence unit whose name starts with the specified persistence unit name. Once declared, this customized persistence unit manager should be declared in the EntityManagerFactoryBean with the name of the application’s persistence unit.

A sample configuration of the solution:

In our sample, the factory scans in META-INF/jpa/persistence.xml by default. We didn’t used the default location because these persistence.xml are in fact invalid (only the version built at runtime is) and several application servers are scanning these files for consistency on startup. For instance, Weblogic validates that each persistence unit has a unique name (this is why we initially implemented the strict mode) and JBoss 5+ validates other fields like the coherence between the transaction-type and the specified data-source. Note also that we reused our convenient JtaPersistenceUnitPostProcessor that was described already in a previous article.

Each persistence unit in a module can either declare the entities that need to be added to the application‘s persistence unit or can be left blank, which will scan all the declared entities in that particular module.

If your application configures JPA with Spring, you might found this little add-on useful as it allows you to define a modular  persistence unit. On a final note, please make sure to use either Spring 2.5+ or Spring 3.0.3+ as a regression was introduced in Spring 3.0.1 (see SPR-7055).

6 comments to Configuring modular JPA applications with Spring

  • great post. i want to share also a basic tutorial on using annotations on hibernate with spring

    http://www.adobocode.com/spring/spring-with-hibernate-annotations

    hope this helps

  • Nehemia Litterat

    You saved me many hours of work. The example project is way over the board help.
    Many many thanks.

  • Barny Taylor

    Hi

    I was downloaded the source code, and tried this in my project, but I got an exception:
    Caused by: java.lang.IllegalArgumentException: Not an entity: class hu.enwebem.domain.User
    at org.hibernate.ejb.metamodel.MetamodelImpl.entity(MetamodelImpl.java:158)
    at org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation.(JpaMetamodelEntityInformation.java:52)
    at org.springframework.data.jpa.repository.support.JpaPersistableEntityInformation.(JpaPersistableEntityInformation.java:40)
    at org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.getMetadata(JpaEntityInformationSupport.java:59)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getEntityInformation(JpaRepositoryFactory.java:145)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:83)
    at org.springframework.data.jpa.repository.support.JpaRepositoryFactory.getTargetRepository(JpaRepositoryFactory.java:66)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:146)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:120)
    at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.getObject(RepositoryFactoryBeanSupport.java:39)
    at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:142)

    Class “hu.enwebem.domain.User” in maven submodule, and I got it, when Spring want to autowire UserRepository (equivalent with UserDao)

    Have you got any idea what I did wrong?

    Thanks

    • Without more details, I am afraid I can’t help much. The entity has not been detected by Hibernate. Did you make sure to name your persistence unit in such a way it can be scanned? Note that the persistence units are scanned from META-INF/jpa/persistence.xml (vs. META-INF/persistence.xml)

      If that does not solve your issue, a debug log on com.bsb.showcase.jpa might help.

      HTH,
      S.

      • This will allow for a common line of code to be much more crbgifuoanle on a per-deployment basis awesome!One thing, though It’s probably best to use lazy-initialization on the SpringBeans to allow the overrides ( which are processed last ) to be the only implementation ever instantiated. Otherwise there may be crufty chaos and side-effects when unexpected resources are loaded, for example. Or if the overriden bean attached itself as a callback with another bean through some mechanism

Leave a Reply

  

  

  


7 − six =

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">