The ability to dynamically switch the dynamic level in the application (JPA)

The data access level of applications is built using Spring and EclipseLink, and I'm currently trying to implement the following function - The ability to dynamically switch the current / active save block for the user. I tried various options and finally finished doing the following.

In the persistence.xml file, declare several PUs. Create a class with as many EntityManagerFactory attributes as the PU. This will act as a factory and return the appropriate EntityManager based on my logic

public class MyEntityManagerFactory { @PersistenceUnit(unitName="PU_1") private EntityManagerFactory emf1; @PersistenceUnit(unitName="PU_2") private EntityManagerFactory emf2; public EntityManager getEntityManager(int releaseId) { // Logic goes here to return the appropriate entityManeger } } 

My spring - beans xml looks like this.

 <!-- First persistence unit --> <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="emFactory1"> <property name="persistenceUnitName" value="PU_1" /> </bean> <bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager1"> <property name="entityManagerFactory" ref="emFactory1"/> </bean> <tx:annotation-driven transaction-manager="transactionManager1"/> 

The above section is repeated for the second PU (with names such as emFactory2, transactionManager2, etc.).

I am new to JPA and I know that this is not the best solution. I appreciate any help in fulfilling this requirement more / elegantly!

Thanks!

+4
source share
3 answers

First of all, thanks to user332768 and bert. I tried using AbstractRoutingDataSource, as mentioned in the link provided by bert, but got lost trying to connect my jpa (eclipselink) layer. I returned to my earlier approach with some changes. The solution looks cleaner (IMHO) and works fine. (switching the database at runtime, as well as writing to multiple databases in one transaction)

 public class MyEntityManagerFactoryImpl implements MyEntityManagerFactory, ApplicationContextAware { private HashMap<String, EntityManagerFactory> emFactoryMap; public EntityManager getEntityManager(String releaseId) { return SharedEntityManagerCreator.createSharedEntityManager(emFactoryMap.get(releaseName)); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { Map<String, LocalContainerEntityManagerFactoryBean> emMap = applicationContext.getBeansOfType(LocalContainerEntityManagerFactoryBean.class); Set<String> keys = emMap.keySet(); EntityManagerFactory entityManagerFactory = null; String releaseId = null; emFactoryMap = new HashMap<String, EntityManagerFactory>(); for (String key:keys) { releaseId = key.split("_")[1]; entityManagerFactory = emMap.get(key).getObject(); emFactoryMap.put(releaseId, entityManagerFactory); } } } 

Now I insert my DAO with an instance (singleton) of MyEntityManagerFactoryImpl. Then dao will simply call createSharedEntityManager with the required release and get the correct EntityManager for this database. (Note that now I am using the application-driven EntityManager and therefore I must explicitly close them in my dao)

I also went to jta transaction manager (to manage transactions across multiple databases) Here is what my spring xml looks like.

 ... <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="em_Rel1"> <property name="persistenceUnitName" value="PU1" /> </bean> <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="em_Rel2"> <property name="persistenceUnitName" value="PU2" /> </bean> <bean class="org.springframework.transaction.jta.JtaTransactionManager" id="jtaTransactionManager"> </bean> <tx:annotation-driven transaction-manager="jtaTransactionManager"/> .... 

Hurrah! (comments are welcome)

+1
source

I am not sure if this is a clean method. Instead of declaring an enitiymanagerfactory multiple times, we can use the spring application context to get the entitymanagerfactory declared in spring application.xml.

 hm = applicationContext.getBeansOfType(org.springframework.orm.jpa.LocalEntityManagerFactoryBean.class); EntityManagerFactory emf = ((org.springframework.orm.jpa.LocalEntityManagerFactoryBean) hm.get("&emf1")).getNativeEntityManagerFactory(); EntityManagerFactory emf2 = ((org.springframework.orm.jpa.LocalEntityManagerFactoryBean) hm.get("&emf2")).getNativeEntityManagerFactory(); 
0
source

This is what I need to do in the future, for this I bookmarked Spring DynamicDatasourceRouting

http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/

As far as I understand, this uses a single PU, which is assigned different data sources. Perhaps this is helpful.

0
source

Source: https://habr.com/ru/post/1308775/


All Articles