How to inject persistence context to different data source programmatically

前端 未结 4 1084
时光说笑
时光说笑 2020-12-03 06:01

In standard EJB 3, when injecting entity manager, persistence unit (which refers to datasource) is hardcoded into annotation: (or alternatively xml file)

@Pe         


        
4条回答
  •  伪装坚强ぢ
    2020-12-03 06:33

    It is possible! I've done it and it works under JBoss AS and WebSphere.

    I use a custom persistence provider which extends org.hibernate.ejb.HibernatePersistence (you need to modify a private static final field to set your persistence provider name into org.hibernate.ejb3.Ejb3Configuration.IMPLEMENTATION_NAME: this is a kind of black magic but it works). Make sure your persistence.xml's persistence units have the custom provider set in the tag and your custom provider is registered in META-INF/services/javax.persistence.spi.PersistenceProvider.

    My provider overrides the createContainerEntityManagerFactory(PersistenceUnitInfo,Map) method called the Java EE container as such (for JTA datasource but it would be easy to do it also for non JTA datasource):

    @Override
    public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) {
    
        // load the DataSource
        String newDataSourceName = ...; // any name you want
        DataSource ds = (DataSource)(new InitialContext().lookup(newDataSourceName));
    
        // modify the datasource
        try {
            try {
                // JBoss implementation (any maybe other Java EE vendors except IBM WebSphere)
                Method m = info.getClass().getDeclaredMethod("setJtaDataSource", DataSource.class);
                m.setAccessible(true);
                m.invoke(info, ds);
    
            } catch (NoSuchMethodException e) {
                // method does not exist (WebSphere?) => try the WebSphere way
    
                // set the datasource name
                Method m = info.getClass().getDeclaredMethod("setJtaDataSource", String.class);
                m.setAccessible(true);
                m.invoke(info, newDataSourceName);
    
                // do the lookup
                Method m2 = info.getClass().getDeclaredMethod("lookupJtaDataSource", String.class);
                m2.setAccessible(true);
                m2.invoke(info);
            }
        } catch (Throwable e) {
            throw new RuntimeException("could not change DataSource for "+info.getClass().getName());
        }
    
        // delegate the EMF creation
        return new HibernatePersistence().createContainerEntityManaferFactory(info, map);
    }
    

    The createEntityManagerFactory(String,Map) also overriden but is much simpler:

    @Override
    public EntityManagerFactory createEntityManagerFactory(String persistenceUnitInfo, Map map) {
    
        // change the datasource name
        String newDataSourceName = ...; // any name you want
        if (map==null) map = new HashMap();  
        map.put(HibernatePersistence.JTA_DATASOURCE, newDataSourceName);  
    
        // delegate the EMF creation
        return new HibernatePersistence().createEntityManaferFactory(persistenceUnitInfo, map);
    }
    

    Note that I only wrote here the core code. In fact, my persistence provider has a lot of other functionalities:

    • check that the DataSource is up and running
    • set the transaction manager for JBoss or WebSphere
    • cache the EMF for lower memory usage
    • reconfigure the Hibernate query plan cache for smaller memory usage
    • register JMX bean (to allow more than one EAR to get the same persistence unit name)

提交回复
热议问题