Community   SpringSource   Projects    Downloads    Documentation    Forums    Training   Exchange   Blogs

Go Back   Spring Community Forums > Core Spring Projects > Data Access

Reply
 
Thread Tools Display Modes
  #1  
Old Sep 23rd, 2008, 03:20 PM
rdelaplante rdelaplante is offline
Member
 
Join Date: Aug 2008
Posts: 54
Default @PesistenceContext with multiple JPA+JTA persistence units

Hi,

I have spent all day reading forums, blogs and documentation on this topic without any luck. What I want to do is:

- I have a PostgreSQL database and an SQL Server database
- I have two separate persistence.xml files (different filenames), and two entityManagerFactory beans defined:

Code:
<context:load-time-weaver/>
    <context:annotation-config/>
    <tx:annotation-driven/>
    <tx:jta-transaction-manager  />
    
    <bean id="webCicoEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="WebCicoPU"/>
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
					p:databasePlatform="oracle.toplink.essentials.platform.database.PostgreSQLPlatform" p:showSql="false" />
        </property>
    </bean>

    <bean id="centralDataEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="CentralDataPU"/>
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence-CentralDataPU.xml"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
					p:databasePlatform="oracle.toplink.essentials.platform.database.SQLServerPlatform" p:showSql="false" />
        </property>
    </bean>
- I have two DAO objects. In the first I use:

Code:
    @PersistenceContext(name = "WebCicoPU")
    private transient EntityManager entityManager;
in the second I use:

Code:
    @PersistenceContext(name = "CentralDataPU")
    private transient EntityManager entityManager;
Notice that there is no mention of datasource. Spring automatically picks it up from the persistence.xml (JNDI). JPA+JTA work fine when using just a single persistence unit but now that I need to use two, I get the following exception:


Spring gives the following exception:

Caused by: org.springframework.beans.factory.NoSuchBeanDefini tionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2


How do I tell which entity manager factory to use per DAO object?


This thread did not help:

http://forum.springframework.org/sho...d+single+found


Thanks,
Ryan
Reply With Quote
  #2  
Old Sep 24th, 2008, 09:54 AM
rdelaplante rdelaplante is offline
Member
 
Join Date: Aug 2008
Posts: 54
Default

I've seen some threads talk about using a DefaultPersistenceUnitManager and sharing them between all LocalContainerEntityManagerFactoryBean instances. I still get the same problem where @PersistenceContext(unitName="foobar") doesn't know which instance of javax.persistence.EntityManagerFactory to inject. Is there a way for me to inject an EntityManager loaded from the appropriate factory using Spring's XML instead of the @PersistenceContext annotation?

Code:
<bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
        <property name="persistenceXmlLocations">
            <list>
                <value>classpath:META-INF/persistence-CentralDataPU.xml</value>
                <value>classpath:META-INF/persistence.xml</value>
            </list>
        </property>
    </bean>

    <bean id="webCicoEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitManager" ref="persistenceUnitManager"/>
        <property name="persistenceUnitName" value="WebCicoPU"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
					p:databasePlatform="oracle.toplink.essentials.platform.database.PostgreSQLPlatform" p:showSql="false" />
        </property>
    </bean>

    <bean id="centralDataEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitManager" ref="persistenceUnitManager"/>
        <property name="persistenceUnitName" value="CentralDataPU"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
					p:databasePlatform="oracle.toplink.essentials.platform.database.SQLServerPlatform" p:showSql="false" />
        </property>
    </bean>

Thanks,
Ryan
Reply With Quote
  #3  
Old Sep 24th, 2008, 10:45 AM
rdelaplante rdelaplante is offline
Member
 
Join Date: Aug 2008
Posts: 54
Default @PesistenceContext with multiple JPA+JTA persistence units in GlassFish

I have found a hack to make this work. I call it a hack because Spring 2.5 does not properly implement support for JPA and therefore I have to do extra work to support multiple persistence units. I've used multiple persistence units with EJB 3.0 before and it was completely effortless. It just worked. With Spring I had to spend over a day to get to this point.

Anyway, onto the solution:

1) Spring doesn't support multiple persistence unit inside the same persistence.xml, so you have to create a separate XML file for each persistence unit.

2) You need to create a separate LocalContainerEntityManagerFactoryBean for each persistence unit. Use these properties to configure them:

<property name="persistenceUnitName" value="MyGreatPU"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence-MyGreatPU.xml"/>

3) You cannot use @PersistenceUnit(unitName = "foo") or @PersistenceContext(unitName = "foo") in your DAOs because Spring doesn't know which LocalContainerEntityManagerFactoryBean to inject. It doesn't matter that you've already specified the persistence unit name in Spring's XML configuration AND in the annotation. Instead, you need to create a setter method in your DAO for an EntityManagerFactory, and in your Spring XML configure your DAO bean to have the appropriate LocalContainerEntityManagerFactoryBean injected. In this setter method I use the factory to create an EntityManager and store that in a private class level field.

4) In every method of your DAO that is an entry point from the point of view of transactions, you need to call entityManager.joinTransaction();


Below is what my applicationContext.xml looks like. I hope I was able to help other people trying to solve this problem. If anyone knows of a better way to do this then please let me know!


Thanks,
Ryan


Code:
    <context:load-time-weaver/>
    <context:annotation-config/>
    <tx:annotation-driven/>
    <tx:jta-transaction-manager  />
    
    <bean id="webCicoEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="WebCicoPU"/>
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
					p:databasePlatform="oracle.toplink.essentials.platform.database.PostgreSQLPlatform" p:showSql="false" />
        </property>
    </bean>

    <bean id="centralDataEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="CentralDataPU"/>
        <property name="persistenceXmlLocation" value="classpath:META-INF/persistence-CentralDataPU.xml"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
					p:databasePlatform="oracle.toplink.essentials.platform.database.SQLServerPlatform" p:showSql="false" />
        </property>
    </bean>

    <bean id="WebCicoSettingsDAO" class="com.ijws.webcico.dao.settings.WebCicoSettingsDAOImpl" scope="singleton">
        <property name="entityManagerFactory" ref="webCicoEntityManagerFactory"/>
    </bean>

    <bean id="CentralReservationsDAO" class="com.ijws.webcico.dao.centraldata.CentralReservationsDAOImpl" scope="singleton">
        <property name="entityManagerFactory" ref="centralDataEntityManagerFactory"/>
    </bean>
Reply With Quote
  #4  
Old Sep 24th, 2008, 01:41 PM
rdelaplante rdelaplante is offline
Member
 
Join Date: Aug 2008
Posts: 54
Post

Next I moved onto my unit test environment and have more problems. My unit test environment does not use JTA or JNDI data sources. It has two datasources defined in the applicationContext.xml, and the entityManagerFactory beans point to the appropriate one. I've also created separate JpaTransactionManager's for each of the entiyManagerFactories. As you can see from my Spring XML below, the entityManagerFactories *clearly* state which data source to use:

Code:
    <context:annotation-config/>
    <context:property-placeholder location="META-INF/jdbc.properties"/>

    <bean id="webCicoDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClass}"/>
        <property name="url" value="${jdbc.url.test}"/>
        <property name="username" value="${jdbc.user.test}"/>
        <property name="password" value="${jdbc.password.test}"/>
    </bean>

    <bean id="centralDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver"/>
        <property name="url" value="jdbc:jtds:sqlserver://vault/CMS;instance=sql2005"/>
        <property name="username" value="blah"/>
        <property name="password" value="blahblah"/>
    </bean>

	<bean id="webCicoEntityManagerFactory" class= "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="webCicoDataSource"/>
        <property name="persistenceXmlLocation" value="META-INF/persistence.xml"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
                p:databasePlatform="oracle.toplink.essentials.platform.database.PostgreSQLPlatform" p:showSql="true"/>
        </property>
    </bean>

    <bean id="centralDataEntityManagerFactory" class= "org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="centralDataSource"/>
        <property name="persistenceXmlLocation" value="META-INF/persistence-CentralDataPU.xml"/>
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
                p:databasePlatform="oracle.toplink.essentials.platform.database.PostgreSQLPlatform" p:showSql="true"/>
        </property>
    </bean>

    <bean id = "webCicoTransactionManager" class= "org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="webCicoEntityManagerFactory"/>
    </bean>

    <bean id = "centralDataTransactionManager" class= "org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="centralDataEntityManagerFactory"/>
    </bean>

    <tx:annotation-driven transaction-manager="webCicoTransactionManager"/>
I think there are two problems:

1) When Spring initializes it says: No unique bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: [webCicoDataSource, centralDataSource]

2) Am I supposed to write this twice, once for each transaction manager?
<tx:annotation-driven transaction-manager="webCicoTransactionManager"/>

I don't think so. According to this bug ticket, Spring doesn't support @Transactional with multiple transaction managers:

http://jira.springframework.org/browse/SPR-3955

Any help would be appreciated
Reply With Quote
  #5  
Old Sep 24th, 2008, 02:15 PM
rdelaplante rdelaplante is offline
Member
 
Join Date: Aug 2008
Posts: 54
Default

It looks like I'm going to have to separate the settings for each datasource/entitymanager/transaction manager into separate XML configuration files, and only load the one I need for the particular unit test.

This is bad news when it comes to higher level integration testing where a service class uses both DAOs and therefore needs both databases.

Another option is to use XML to apply transactions to the classes that use @Transactional annotation. Will I have to remove the @Transactional annotation to make it work? If yes then I'll have to also use XML configuration in the non test environment too.

Just a side note, I've only been using Spring for a month or so after having read a book on Spring 2.0 cover-to-cover. I want to use it, I want to like it. Every single thing I've tried to do with Spring so far has given me blue balls and black eyes. I have wasted literally weeks because of it's over complications of everything. I swear EJB 3.0 is easier. I just wish it would work in a web container. When EJB 3.1 comes out I'm going to clean up this Spring mess and migrate back. Hopefully EJB 3.1's unit testing support works the same as it does in an application server without much effort.
Reply With Quote
  #6  
Old Sep 25th, 2008, 10:23 AM
rdelaplante rdelaplante is offline
Member
 
Join Date: Aug 2008
Posts: 54
Default

I'll describe the latest news. Earlier I mentioned that the web app running in GlassFish works when I don't use @PersitenceContext or @PersistenceUnit. Instead I use spring XML to inject the right entityManagerFactory bean. The setter method calls .createEntityManager and stores it in a class level field, then any method that uses it calls .joinTransaction().

In my unit test environment I have created two separate applicationContext.xml files. One for beans that use the first database, and one for beans that use the second database. Spring no longer complains about not knowing which DataSource instance to use because there is only one per file. My tests are kind of able to use the database but it keeps locking up somewhere on me and I have to kill the process. If I comment out the changes to my DAO that creates its own entityManager from a factory and joins the transaction, and replace it with the old @PersistenceContext then the unit tests work fine!

So in my real app I can't use @PersitenceContext, but in my unit tests I must use @PersistenceContext. F*cked if you do, F*cked if you don't. It looks like I can't do any unit tests with Spring and JPA on this project.
Reply With Quote
  #7  
Old Sep 26th, 2008, 01:34 AM
magnusheino magnusheino is offline
Member
 
Join Date: Feb 2005
Posts: 46
Default

What are you doing? I've not read through all your posts, but...

Revert your latest changes, this works just fine.

Go and read the javadocs for PersistenceContext, you should use PersistenceContext(unitName="whatever") not PersistenceContext(name="whatever").

/Magnus
Reply With Quote
  #8  
Old Sep 26th, 2008, 03:21 AM
rdelaplante rdelaplante is offline
Member
 
Join Date: Aug 2008
Posts: 54
Default

I must have made a typo in the forum but I'm pretty certain I have always specified unitName="whatever" when using @PersistenceContext and @PersistenceUnit. Further up in this thread I mentioned my findings when working in the application server environment:

Quote:
1) Spring doesn't support multiple persistence unit inside the same persistence.xml, so you have to create a separate XML file for each persistence unit.

2) You need to create a separate LocalContainerEntityManagerFactoryBean for each persistence unit. Use these properties to configure them:

<property name="persistenceUnitName" value="MyGreatPU"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence-MyGreatPU.xml"/>

3) You cannot use @PersistenceUnit(unitName = "foo") or @PersistenceContext(unitName = "foo") in your DAOs because Spring doesn't know which LocalContainerEntityManagerFactoryBean to inject. It doesn't matter that you've already specified the persistence unit name in Spring's XML configuration AND in the annotation. Instead, you need to create a setter method in your DAO for an EntityManagerFactory, and in your Spring XML configure your DAO bean to have the appropriate LocalContainerEntityManagerFactoryBean injected. In this setter method I use the factory to create an EntityManager and store that in a private class level field.

4) In every method of your DAO that is an entry point from the point of view of transactions, you need to call entityManager.joinTransaction();
In my unit test environment I specify dataSources inside the Spring XML config. Since I specified 2 datasources, Spring doesn't know which one to inject into my LocalContainerEntityManagerFactoryBean beans, saying:

Quote:
No unique bean of type [javax.sql.DataSource] is defined: expected single matching bean but found 2: [webCicoDataSource, centralDataSource]
The only solution I could find was to create two applicationContext.xml files for my unit test environment. One with information for the first database, and a second with information for the second database. Since my code was changed to no longer use @PersistenceContext annotations, for some reason my unit tests hang when using the database. If I change my code to use the @PersistenceContext annotations then unit tests begin to work, but the app won't run in an app server because the non-unit test environment has two entity managers which is not supported by @PersistenceContext


Thanks,
Ryan
Reply With Quote
  #9  
Old Sep 26th, 2008, 05:16 PM
hplusplus hplusplus is offline
Junior Member
 
Join Date: Dec 2005
Posts: 5
Default

Typically, you should let the container bootstrap the persistence unit and acquire the persistence unit reference from the container’s JNDI, LocalContainerEntityManagerFactoryBean products are not available to your non-spring beans.
Reply With Quote
  #10  
Old Sep 26th, 2008, 05:33 PM
rdelaplante rdelaplante is offline
Member
 
Join Date: Aug 2008
Posts: 54
Default

Thanks for the reply, but I just finished saying that I was using the Spring container to bootstrap the persistence unit using @PersistenceContext(unitName="blah") until I had to add a second persistence unit. Spring's implementation of @PersistenceContext does not know which entity manager to inject, which forced me to use XML to manually inject the correct factory, manually create an entity manager using that factory, and join the active transaction.

I am loading the datasource from JNDI, and it works fine in the app server. I said in my unit tests there is no JNDI so I have to define data sources in my Spring XML file. Spring complains because it doesn't know which data source to inject into the entity manager factory even after I explicitly told it which one to use!

So, nothing new here yet.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 10:08 AM.


Contegix provides first-class managed hosting and partial sponsorship of these forums.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.