Routing Read-Write transactions to Primary and Read_only transactions to Replicas using Spring and Hibernate

前端 未结 4 1782
没有蜡笔的小新
没有蜡笔的小新 2021-01-31 12:46

I have an application that uses Hibernate/JPA, with Spring and Jersey. In my application context I set the data source, define an entity manager factory, set the transaction man

4条回答
  •  孤城傲影
    2021-01-31 13:24

    Here's what I ended up doing and it worked quite well. The entity manager can only have one bean to use as the data source. So what I had to do was to create a bean that routed between the two where necessary. That one ben is the one I used for the JPA entity manager.

    I setup two different data sources in tomcat. In the server.xml I created two resources (data sources).

    
    
    

    You could have the database tables on the same server, in which case the ip addresses or domain would be the same, just different dbs - you get the jist.

    I then added a resource link in the context.xml file in tomcat that referenced these to resources.

    
    
    

    These resource links are what spring reads in the application context.

    In the application context I added a bean definition for each resource link and added one additional bean definition that referenced a Datasource Router bean I created that takes in a map (enum) of the two previously created beans (bean definition).

    
    
         
        
        
        
          
    
    
    
        
        
        
        
        
    
    
    
    
        
          
             
             
          
       
       
    
    

    The entity manager bean definition then referenced the dataSource bean.

    
        
        
         
             
                
                
            
        
    
    

    I defined some properties in a properties file, but you can replace the ${} values with your own specific values. So now I have one bean that uses two other beans that represent my two data sources. The one bean is the one I use for JPA. It's oblivious of any routing happening.

    So now the routing bean.

    public class DatasourceRouter extends AbstractRoutingDataSource{
    
        @Override
        public Logger getParentLogger() throws SQLFeatureNotSupportedException{
        // TODO Auto-generated method stub
        return null;
        }
    
        @Override
        protected Object determineCurrentLookupKey(){
        return DatasourceProvider.getDatasource();
        }
    
    }
    

    The overridden method is called by the entity manager to determine the data source basically. The DatasourceProvider has a thread local (thread safe) property with a getter and setter method as well as the clear data source method for clean up.

    public class DatasourceProvider{
        private static final ThreadLocal datasourceHolder = new ThreadLocal();
    
        public static void setDatasource(final AvailableDataSources customerType){
        datasourceHolder.set(customerType);
        }
    
        public static AvailableDataSources getDatasource(){
        return (AvailableDataSources) datasourceHolder.get();
        }
    
        public static void clearDatasource(){
        datasourceHolder.remove();
        }
    
    }
    

    I have a generic DAO implementation with methods I use to handle various routine JPA calls (getReference, persist, createNamedQUery & getResultList, etc.). Before it makes the call to the entityManager to do whatever it needs to do I set the DatasourceProvider's datasource to the read or write. The method can handle that value being passed in as well to make it a little more dynamic. Here is an example method.

    @Override
    public List findByNamedQuery(final String queryName, final Map properties, final int... rowStartIdxAndCount)
    {
    DatasourceProvider.setDatasource(AvailableDataSources.READ);
    final TypedQuery query = entityManager.createNamedQuery(queryName, persistentClass);
    if (!properties.isEmpty())
    {
        bindNamedQueryParameters(query, properties);
    }
    appyRowLimits(query, rowStartIdxAndCount);
    
    return query.getResultList();
    }
    

    The AvailableDataSources is an enum with READ or WRITE, which references the appropriate data source. You can see that in the map defined in my bean on the application context.

提交回复
热议问题