Spring AOP approach for dynamically switching data sources

Source: Internet
Author: User
Tags aop log4j
General Statement
Projects sometimes encounter multiple data sources, that is, in a service to operate two or more than two databases, it is necessary to timely and accurate data source switching.
In general, it is possible to set a default data source to specify a new data source where the service needs to switch the data source, but there are two disadvantages:
(1) Too many ways to specify new data sources will affect code clarity and reduce the readability of the Code;
(2) If the service has turned on the transaction, the data source switchover failed.
In addition to the methods described above, you can also dynamically switch data sources through spring's AOP.
Configuring multiple data sources
Here the case is illustrated in the context of spring and mybatis integration.
1. Configure multiple data sources in Spring-datasource.xml
<!--Firstdatasource--<bean id= "Firstdatasource" class= "Com.alibaba.druid.pool.DruidDataSource" Init-method= "Init" destroy-method= "close" > <property name= "url" value= "${firstdatasource.url}"/> < Property name= "username" value= "${firstdatasource.name}"/> <property name= "password" value= "${ Firstdatasource.password} "/> <property name=" Filters "value=" log4j "/> <property name=" maxActive "value="
	${firstdatasource.maxactive} "/> <property name=" initialsize "value=" ${firstdatasource.initialisize} "/> <property name= "maxwait" value= "60000"/> <property name= "Minidle" value= "1"/> <property name= "timeBetw Eenevictionrunsmillis "value=" "/> <property name=" Minevictableidletimemillis "value=" 300000 "/> < Property Name= "Validationquery" value= "select ' X '"/> <property name= "Testwhileidle" value= "true"/> <prope Rty name= "Testonborrow" value= "false"/> <property name= "Testonreturn" value= "false"/> <property name= "poolpreparedstatements" value= "false"/> <property name= " Maxpoolpreparedstatementperconnectionsize "value="/> </bean> <!--seconddatasource---<bean id=
	"Seconddatasource" class= "Com.alibaba.druid.pool.DruidDataSource" init-method= "Init" destroy-method= "Close" > <property name= "url" value= "${seconddatasource.url}"/> <property name= "username" value= "${ Seconddatasource.name} "/> <property name=" password "value=" ${seconddatasource.password} "/> <property Name= "Filters" value= "log4j"/> <property name= "maxactive" value= "${seconddatasource.maxactive}"/> < Property Name= "InitialSize" value= "${seconddatasource.initialisize}"/> <property name= "maxWait" value= "60000"
	/> <property name= "Minidle" value= "1"/> <property name= "timebetweenevictionrunsmillis" value= "/>" <property name= "Minevictableidletimemillis" value= "300000"/> <property name= "ValidatiOnquery "value=" select ' X ' "/> <property name=" Testwhileidle "value=" true "/> <property name=" TestOnBorrow " Value= "false"/> <property name= "Testonreturn" value= "false"/> <property name= "poolpreparedstatements" va "False"/> <property name= "maxpoolpreparedstatementperconnectionsize" value= "lue="/> </bean>
2. Create the class Databasecontextholder, specifying that the default data source is Firstdatasource
public class Databasecontextholder {

	private static final threadlocal<string> Datasource_type = new Threadlocal<string> () {
		@Override
		protected String initialvalue () {
			return ' Firstdatasource ';
		}
	};

	public static void Setdbtype (String datasourcetype) {
		datasource_type.set (datasourcetype);
	}

	public static String Getdbtype () {
		return datasource_type.get ();
	}

}
3. Create a class Dynamicdatasource that inherits Abstractroutingdatasource and implement the Determinecurrentlookupkey method
Import Org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class Dynamicdatasource extends Abstractroutingdatasource {

	@Override
	protected Object Determinecurrentlookupkey () {
		return databasecontextholder.getdbtype ();
	}

}
4. Configure the data source in Spring-datasource.xml, the Class property value is the path to the classes Dynamicdatasource created in the previous step
<bean id= "DataSource" class= "Cn.test.model.common.datasource.DynamicDataSource" >
	<property name= " Defaulttargetdatasource "ref=" Firstdatasource "/>		
	<property name=" targetdatasources ">
		<map Key-type= "java.lang.String" >
			<entry key= "Firstdatasource" value-ref= "Firstdatasource"/>
			< Entry key= "Seconddatasource" value-ref= "Seconddatasource"/>
		</map>
	</property>
</ Bean>
5. Create the class Datasourceinterceptor and add the AOP configuration to the spring configuration file: <aop:aspectj-autoproxy expose-proxy= "true"/>
Import Org.aspectj.lang.JoinPoint;
Import Org.aspectj.lang.annotation.Aspect;
Import Org.aspectj.lang.annotation.Before;
Import Org.aspectj.lang.annotation.Pointcut;
Import Org.springframework.core.annotation.Order;
Import org.springframework.stereotype.Component;

@Aspect
@Component
@Order (0) public
class Datasourceinterceptor {

	@Pointcut ("Execution (* Cn.test.model. service.impl.*firstserviceimpl.* (..)) ")
	public void Firstdatasource () {
	};
	
	@Pointcut ("Execution (* Cn.test.model). service.impl.*secondserviceimpl.* (..)) ")
	public void Seconddatasource () {
	};

	@Before ("Firstdatasource ()") Public
	void Beforefirst (Joinpoint jp) {
		Databasecontextholder.setdbtype (" Firstdatasource ");
	}
	
	@Before ("Seconddatasource ()") Public
	void Beforesecond (Joinpoint jp) {
		Databasecontextholder.setdbtype (" Seconddatasource ");
	}

}
Configuring Transactions
Configure the transaction on the service's implementation class
<bean id= "TransactionManager" class= "Org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name= "DataSource" ref= "DataSource"/> </bean> <tx:annotation-driven/> <tx:advice id= " Txadvice "transaction-manager=" TransactionManager "> <tx:attributes> <tx:method name=" find* "propagation = "REQUIRED" read-only= "true"/> <tx:method name= "query*" propagation= "REQUIRED" read-only= "true"/> <tx:me Thod name= "list*" propagation= "REQUIRED" read-only= "true"/> <tx:method name= "search*" propagation= "REQUIRED" Read-only= "true"/> <tx:method name= "get*" propagation= "REQUIRED" read-only= "true"/> <tx:method name= "sav e* "propagation=" REQUIRED "/> <tx:method name=" add* "propagation=" REQUIRED "/> <tx:method name=" update* "p ropagation= "REQUIRED"/> <tx:method name= "delete*" propagation= "REQUIRED"/> <tx:method name= "create*" pro pagation= "REQUIRED"/> <tx:method name= "check* "propagation=" REQUIRED "/> <tx:method name=" sync* "propagation=" REQUIRED "/> <tx:method name=" Execu te* "propagation=" REQUIRED "/> <tx:method name=" * "propagation=" SUPPORTS "/> </tx:attributes> </tx:a dvice> <aop:config> <aop:pointcut id= "interceptorpointcuts" expression= "Execution (* Cn.test.model). service.impl.*serviceimpl.* (..)) " /> <aop:advisor advice-ref= "Txadvice" pointcut-ref= "Interceptorpointcuts" order= "1"/> </aop:config>
The switching of data sources and the opening of transactions are implemented through AOP, where the tangency point is configured at the same level as possible (class); It is important to note that the switching of the data source must precede the opening of the transaction.
Precautions
The code takes the form of inter-servic calls. When a method in *firstserviceimpl SaveData () calls the *secondservice method, the slice of the data source is first intercepted to modify the data source, and then the transaction is opened.
The problem that may arise is that when the *secondservice method finishes executing back to *firstserviceimpl, the data source will have an error if *firstserviceimpl then needs to continue firstdatasource.
This is because the slice that modifies the data source is not triggered at this point, and the data source still stays in seconddatasource, even if the method is called *firstserviceimpl itself.
How to solve this problem. You can split the code that needs to be executed into a new service, but it will break the overall structure of the code, and a better way is to extract the subsequent code as an interface to *firstservice and implement it in *firstserviceimpl. It then uses Org.springframework.aop.framework.AopContext to invoke the interface in *firstserviceimpl, such as:
((Datafirstservice) Aopcontext.currentproxy ()). UpdateData ();

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.