SSH dynamic data source switching, the transaction scenarios using AOP

Last week, write code method having trouble switching data source, do some operations to two different data sources in the same method, but this method uses a transaction, so the Internet is generally dynamically switch data sources becomes ineffective. + Frame is spirngmvc Hibernate, Oracle database, connection pool druid. 
In general, the operation data are handled within the DAO. One approach is to use multiple processing DataSource and then create multiple SessionFactory, processed through different SessionFactory when using Dao layer, but this invasive obvious, under normal circumstances we are using inheritance HibernateSupportDao encapsulation of the If multiple 
SessionFactor this process is a comparison of trouble, revised estimates also find many places. The last one, which is the implementation class AbstractRoutingDataSource use of dynamic data sources through the use of our AOP or manually processed, so less invasive, very good to meet the needs of use. For example, we want to separate read and write data synchronization or other business scenarios.

Let's look at the picture

      

  • Single-source scene (general Web project this configuration process, it has been better able to meet our business needs)
  • Multiple data sources such as a multi-scene SessionFactory estimated as just beginning to imagine like processing operations in a case where the frame, a plurality SessionFactory, Dao then requests for a particular layer, can be treated by a specific effect such SessionFactory business needs, but this process has brought a lot of inconvenience, JDBC programming we prefer to directly use the package at all in many cases, or use Mybatis handle such business scenarios
  • Use AbstractRoutingDataSource implementation class, flexible switch, you can set the current DataSource by AOP or manually programmed, without modifying our preparation for the succession to modify HibernateSupportDao implementation class, so the write way better, as the realization of the principle of them, let I thin to have been. We want to see how to apply, implement the principle slow to speak!
  • Write AbstractRoutingDataSource implementation class, DataSourceContextHolder is available to tools like our dynamic selection of data sources, we are here to write a to select the data source based on the current thread, then block specific comments by AOP, setting the current data source information, you can also manually set the current data source, in the programming class.

/ ** 
 * Class name: DynamicDataSource.java 
 * Function: Dynamic Data source class 
 * / 
public class DynamicDataSource the extends AbstractRoutingDataSource { 
	/ * 
	 * This method must be overridden method is to obtain the current database according to the database identifier 
	 * / 
	@Override 
	protected Object determineCurrentLookupKey () { 
		the DataSourceType type = DataSourceContextHolder .getType (); 
		return type; 
	} 

	public void setDataSourceLookup (dataSourceLookup dataSourceLookup) { 
		super.setDataSourceLookup (dataSourceLookup); 
	} 
 
	public void setDefaultTargetDataSource (Object defaultTargetDataSource) {
		super.setDefaultTargetDataSource (defaultTargetDataSource); 
	}
	
	public void setTargetDataSources(Map targetDataSources) {
		super.setTargetDataSources(targetDataSources);
	}
}
  • Set dynamically selected Datasource, where the AOP Set method can be left to call, or leave us a specific layer or Service layer Dao manually invoked before executing SQL statements.
/ ** 
 * get and set context class is responsible for changing the name of the context data source, to select a specific data source based on the current thread 
 * / 
public class DataSourceContextHolder { 
   / * Get the current thread * / 
    Private static Final the ThreadLocal <the DataSourceType ;> contextHolder = new ThreadLocal () 
   information / ** AOP to set the current supplied to the data source thread * / 
    public static void setType (the DataSourceType type) { 
        contextHolder.set (type); 
    } 
   / ** supplied to the implementation class AbstractRoutingDataSource by selecting a data source key * / 
    public static the DataSourceType getType () { 
        return contextHolder.get (); 
    } 
  / ** default data source * / 
    public static void Clear () { 
        contextHolder.remove (); 
    } 
}
  • Annotation is provided to intercept the data source, may be provided in specific classes, or specific methods, the DataSourceType is an alias for the current data source identification information for our data source (here an enumeration class).
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicSwitchDataSource {
    DataSourceType type();
}
public enum DataSourceType {
    DATASOURCE_RDP("dataSource_rdp"),
    DATASOURCE_OCM("dataSource_ocm");

    private String type;

    DataSourceType(String type) {
        this.type = type;
    }

    public String type() {
        return type;
    }
}
  • Achieve AOP interception class, by blocking the above comments, before it performs a process to set the currently executing SQL data source information, DataSourceContextHolder.setType (....), Data source information where to obtain information from the above notes we set, if information is not provided with the default data source.
@Component
@Aspect
@Order(1)
public class DataSourceAspect {
    private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

    //@within在类上设置
    //@annotation在方法上进行设置
    @Pointcut("@within(org.jeecgframework.core.annotation.DynamicSwitchDataSource)||@annotation(org.jeecgframework.core.annotation.DynamicSwitchDataSource)")
    public void pointcut() {
    }

    @Before("pointcut()")
    public void doBefore(JoinPoint joinPoint) {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        DynamicSwitchDataSource annotationClass = method.getAnnotation(DynamicSwitchDataSource.class);//获取方法上的注解
        IF (annotationClass == null) { 
            annotationClass = joinPoint.getTarget () getClass () getAnnotation (DynamicSwitchDataSource.class);.. The above class // acquired annotation 
            IF (annotationClass == null) return; 
        } 
        // obtaining annotation information value of the data source 
        the DataSourceType dataSourceKey annotationClass.type = (); 
        IF (dataSourceKey = null!) { 
            // execute SQL to the current operation setting information data source specific 
            DataSourceContextHolder.setType (dataSourceKey); 
        } 
        logger.info .. ( "AOP dynamic switching data source, className" + joinPoint.getTarget () getClass () getName () + "methodName" + method.getName () + "; dataSourceKey:" "?" + dataSourceKey == " default data source ": dataSourceKey.type ());
    }

    @After("pointcut()") 
    Public void the After (the JoinPoint Point) { 
        // clean up the data source the current settings, so the default data source is not affected
        DataSourceContextHolder.clear();
    }
}
  •  Spring configuration data source disposed in the core vessel
<! - configuration data source RDP -> 
    <the bean ID = "dataSource_rdp" class = "com.alibaba.druid.pool.DruidDataSource" = the init-Method "the init" the destroy-Method = "Close"> 
        <Property name = "URL" value = "$ {rdp.url}" /> 
        <Property name = "username" value = "$ {rdp.username}" /> 
        <Property name = "password" value = "$ {} rdp.password "/> 
        <! - the size of the connection initialization -> 
        <Property name =" initialSize "value =" 0 "/> 
        <! - maximum number of connector connection pool -> 
        <Property name =" for maxActive "value =" 50 "/> 
        <! - the minimum connection pool idle -> 
        <Property name =" minIdle "value ="5 "/> 
        <! - the maximum wait time to get a connection -> 
        <Property name =" maxWait "value =" 60000 "/>  
        <! - intervals the frequency of such detection, an idle connection is detected to be closed, the unit milliseconds ->
        <Property name =" timeBetweenEvictionRunsMillis "value =" 60000 "/>
        <! - setting up a connection in a minimum time of survival in the pool, in milliseconds -> 
        <Property name = "minEvictableIdleTimeMillis" value = "300000" /> 
        <! - <Property name = "validationQuery" value = "$ validationQuery.sql} { "/> -> 
        <Property name =" testOnBorrow "value =" to false "/> 
        <Property name =" testOnReturn "value =" to false "/> 
        <Property name =" testWhileIdle "value =" to true "/> 
        <! - statistical monitoring feature is turned on Druid's -> 
        <Property name =" Filters "value =" STAT "/> 
        <! - open removeAbandoned function -> 
        <Property name =" removeAbandoned "value =" true "/>
        <! - 1800 seconds, which is 30 minutes -> 
        <Property name = "logAbandoned" value = "to true" />
        <property name="removeAbandonedTimeout" value="3600"/>
        <! - output connection error log off abanded ->
            <map key-type="org.jeecgframework.core.extend.datasource.DataSourceType">
        <-! Oracle connection is acquired Field Comment -> 
        <Property name = "connectProperties"> 
            <The props> 
                <prop Key = "remarksReporting"> to true </ prop> 
            </ The props> 
        </ Property> 
    </ the bean> 

    < ! - configuration data source OCM -> 
    <the bean ID = "dataSource_ocm" class = "com.alibaba.druid.pool.DruidDataSource" 
        ... data source 2 
    </ the bean> 
<-! collection of data sources -> 
    <the bean ID = "the dataSource" class = "org.jeecgframework.core.extend.datasource.DynamicDataSource"> 
        <Property name = "targetDataSources"> 
                <entry Key = "DATASOURCE_RDP" value-ref="dataSource_rdp"/>
                <entry key="DATASOURCE_OCM" value-ref="dataSource_ocm"/>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="dataSource_rdp"/>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="entityInterceptor" ref="hiberAspect"/>
        <property name="hibernateProperties">
            <props>
                <!--<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> -->
                <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
                <!--<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>-->
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
            </props>
        </property>
  • Data source selecting intermediate layer arranged prior to implementation class AbstractRoutingDataSource our implementation, key here is alias data source information, the information may be selected by the data source key. DynamicDataSource implementation class is written above the data source selector.
  • SessionFactory configuration, or as usual, using the previous configuration, only the currently selected data source is datasource, select the data source is an intermediate layer DynamicDataSource, because the current intermediate layer implements the DataSource interface, it can be seen as the DataSource it is the implementation class, so the configuration without problems.
  • Simple to use AOP for test, different results when tested here, it is in force, the use of different data sources, but the underlying implementation without any modification process. (Transactional transactional comment )
  • Look AOP configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"...  >
    <!--设置expose-proxy属性为true,将代理暴露出来-->
    <aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true"/>
    <context:component-scan base-package="org.jeecgframework.core.aop"/>
</beans>

  

@Service("cgFormFieldService")
@Slf4j
@Transactional
public class CgFormFieldServiceImpl implements CgFormFieldServiceI{

    @DynamicSwitchDataSource(type = DataSourceType.DATASOURCE_RDP)
	public void deleteCgForm(CgFormHeadEntity cgFormHead) {
		this.delete(cgFormHead);
		String sql = getTableUtil().dropTableSQL(cgFormHead.getTableName());
//执行ocm中sql ((CgFormFieldServiceImpl) AopContext.currentProxy()).executeOcmSql(sql); } }   @DynamicSwitchDataSource(type = DataSourceType.DATASOURCE_OCM) @Transactional(propagation = Propagation.REQUIRES_NEW) public void executeOcmSql(String sql) { this.executeSql(sql); } }

Transaction isolation environment

If you have already turned in a transaction method A, B calls another method in this transaction method A, if no other configuration, the method B will follow A method of environmental affairs, and will not open a new business. If you do not open a new transaction, of course, it will not carry out a series of changes until the data source is switched. This also proves that they are the same in a transactional environment, because the hibernate session for transaction binding. Since the transaction isolation environment, the following process must be set to a REQUIRES_NEW.

When Service layer, A method calls the method B, with a ((Service) the AopContext.currentProxy ()) B () action:

In the original usage springAOP, only proxy classes will be cut, when we call service method of the controller layer, it can be cut, but if we are in the service layer A method, call B, the cut-off point cut the method is B, then this is not going to cut into the time, the solution is shown above, use ((Service) AopContext.currentProxy ()) in the a method. B () method to call B, so that it You can cut into!

to sum up

These are all about dynamically switch data sources, knowledge mainly related to AOP, transaction management. So once you have the knowledge, want not difficult to handle. Note that point: AbstractRoutingDataSource use of parameterized class enumeration, isolation of things, AOP proxy, etc., want to be able to help everyone. There is nothing permanent!

Guess you like

Origin www.cnblogs.com/mamba-mentality/p/11227654.html