In the previous section, we talked about multiple data scenarios:
1. Different data sources (different database vendors, different business scope, different business data)
2. Unlike SQL mapper files,
3. MyBatis + Data dialect different
That is, the simplest multi-data, overlay multiple data sources together, different service---DAO--->sessionFactory;
If all of the above conditions are the same, just a few copies of the data, want to master (read and write separation), then of course we can use 2 sets of copied code and configuration, but obviously not the best way to implement.
Here, we will explain how to read and write the multiple data sources, how to achieve.
First of all, we read reading (corresponding to Java/getxxx, queryxxx), writing write (corresponding to updatexxx, insertxxx) from the entrance of read/write separation, and if you can automatically switch to read-only database/write-only data when these methods are executed, then you can To realize the separation of read and write, we can naturally think of spring's AOP and implement it in a annotation way.
The specific implementation steps are as follows:
1. Implementing a custom data source annotation: @DataSource ("read")/@DataSource ("write")
1 PackageCom.robin.it.ds;2 3 Importjava.lang.annotation.Retention;4 ImportJava.lang.annotation.Target;5 ImportJava.lang.annotation.RetentionPolicy;6 ImportJava.lang.annotation.ElementType;7 /** 8 * RUNTIME9 * The compiler will record annotations in the class file, and the VM will retain comments at run time, so it can be read in a reflective manner. Ten * @authorRobin.yi One * A */ - - @Retention (retentionpolicy.runtime) the @Target (Elementtype.method) - Public@InterfaceDataSource { - String value (); -}
2. Implement a database selector Choosedatasource
PackageCom.robin.it.ds;ImportOrg.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; Public classChoosedatasourceextendsAbstractroutingdatasource {@OverrideprotectedObject Determinecurrentlookupkey () {returnHandledatasource.getdatasource (); }}============ PackageCom.robin.it.ds; Public classHandledatasource { Public Static Finalthreadlocal<string> holder =NewThreadlocal<string>(); Public Static voidPutdatasource (String datasource) {Holder.set (DataSource); } Public StaticString Getdatasource () {returnHolder.get (); } }
3. Implementation of AOP data interception, switching function
PackageCom.robin.it.ds;ImportJava.lang.reflect.Method; ImportOrg.aspectj.lang.JoinPoint;Importorg.aspectj.lang.reflect.MethodSignature;//@Aspect//@Component Public classDatasourceaspect {//@Pointcut ("Execution (* com.apc.cms.service.*.* (..))") Public voidPointCut () {}; //@Before (value = "pointCut ()") Public voidbefore (Joinpoint point) {Object target=Point.gettarget (); System.out.println (Target.tostring ()); String Method=point.getsignature (). GetName (); System.out.println (method); class<?>[] classz = Target.getclass (). Getinterfaces (); Class<?>[] Parametertypes =((methodsignature) point.getsignature ()). GetMethod (). Getparametertypes (); Try{Method m= Classz[0].getmethod (method, parametertypes); System.out.println (M.getname ()); if(M! =NULL&& m.isannotationpresent (DataSource.class) {DataSource data= M.getannotation (DataSource.class); System.out.println ("==============================={}"+Data.value ()); Handledatasource.putdatasource (Data.value ()); }Else{System.out.println ("000000000000000000000"); } } Catch(Exception e) {e.printstacktrace (); } } }
Note: The Yellow highlighting section, using reflection to read the definition of interface (including annotation's declaration), dynamic reading parameters, data source switching;
4. Defining Interface Services
PackageCom.robin.it.permission.service;ImportCom.robin.it.ds.DataSource;ImportCom.robin.it.permission.module.User;/*** Created by Robin.yi on 2015-4-11.*/ Public InterfaceIuserservice {@DataSource ("Read") PublicUser GetUser (Integer userId); @DataSource ("Read") PublicUser GetUser (string account, string password); @DataSource ("Write") Public voidinsertuser (user user);}
Idea: Above is the statement that determines the library/writing library: @DataSource ("write")/@DataSource ("read")
The implementation of the interface is not specifically described, this omission.
5. The entire spring Applicationcontent.xml file is configured as follows:
<?xml version= "1.0" encoding= "Utf-8"? ><beans xmlns= "Http://www.springframework.org/schema/beans"Xmlns:xsi= "Http://www.w3.org/2001/XMLSchema-instance"Xmlns:context= "Http://www.springframework.org/schema/context"Xmlns:tx= "Http://www.springframework.org/schema/tx"XMLNS:AOP= "HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOP"Xmlns:util= "Http://www.springframework.org/schema/util"xsi:schemalocation= "Http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsdhttp//Www.springframework.org/schema/contexthttp//www.springframework.org/schema/context/spring-context-4.1.xsdhttp//Www.springframework.org/schema/txhttp//www.springframework.org/schema/tx/spring-tx-4.1.xsdhttp//WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOPhttp//www.springframework.org/schema/aop/spring-aop-4.1.xsdhttp//Www.springframework.org/schema/utilhttp//www.springframework.org/schema/util/spring-util-4.1.xsd "><description>This file is used to define the Authority info. </description> <!--various types of annotation bean variables used to scan Spring: @autowired; @Resource-<context:annotation-config/> <!--used to scan a class for a build: such as: @Controller, @Service, @Component-<context:component-scan base- Package= "com.robin.it.permission.*" > <context:exclude-filter type= "annotation" expression= "org.springframework.ste Reotype. Controller "/> </context:component-scan> <tx:annotation-driven/> <bean id= "Configproperties"class= "Org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > <property name= " Ignoreresourcenotfound "value=" false "/> <property name=" Locations "> <list> <!--supports multiple addressing methods: Classpath and File--and <value>classpath:/database.properties</value> <value>classpath:/config.properties</value> <!--is recommended to be introduced using file, which separates configuration and code--& Gt <!--<value>file:/opt/demo/config/demo-message.properties</value>--> </list> < ;/property> </bean> <util:properties id= "props" location= "classpath:/config.properties"/> <!--MYSQL configuration-<bean id= "Readdatasource"class= "Org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name= "driverclassname" >< value>${mysql.jdbc.driverclassname}</value></property> <property name= "url" ><value>${ mysql.jdbc.url}</value></property> <property name= "username" ><value>${ mysql.jdbc.username}</value></property> <property name= "password" ><value>${ mysql.jdbc.password}</value></property> </bean> <bean id= "Writedatasource"class= "Org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name= "driverclassname" >< value>${mysql2.jdbc.driverclassname}</value></property> <property name= "url" ><value>$ {mysql2.jdbc.url}</value></property> <property name= "username" ><value>${ mysql2.jdbc.username}</value></property> <property name= "password" ><value>${ Mysql2.jdbc.password}</value></property> </bean><bean id= "DataSource" class= "Com.robin.it.ds.ChooseDataSource" > <property name= "Targetda Tasources "> <map key-type=" java.lang.String "> <!--write- <entry key= "Write" value-ref= "Writedatasource"/> <!--read-- <entry key= "read" value-ref= "Readdatasource"/> </map> </proper ty> <property name= "Defaulttargetdatasource" ref= "Writedatasource"/> </bean><bean id= "TransactionManager"class= "Org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name= "DataSource" ref= "DataS Ource "/> </bean> <bean id=" Sessionfactory "class= "Org.mybatis.spring.SqlSessionFactoryBean" > <property name= "dataSource" ref= "DataSource"/> <!- -Specify Sqlmapconfig General profile, custom environment not valid in spring container--<property name= "configlocation" value= "Classpath:myb Atis-config-mysql.xml "/> <!--Specify the Entity class mapping file, you can specify that both a package and all configuration files under the child package are specified, mapperlocations and configlocation have one, and when you need to specify an alias for an entity class, you can specify the Configlocation property. Then introduce the Entity class mapping file in the MyBatis general configuration file using Mapper--<property name= "mapperlocations" > <list> <value>classpath*: /mysqlmapper/*mapper.xml</value> </list> </property> </bean> <bean class= "Org.mybat Is.spring.mapper.MapperScannerConfigurer "> <property name=" basepackage "value=" Com.robin.it.permission.dao " /> <!--Optional unless there is multiple session factories defined-<property name= "sqlsess Ionfactorybeanname "value=" Sessionfactory "/> </bean><aop:aspectj-autoproxy proxy-target-class= "true"/> <bean id= "Datasourceaspect" class= "Com.robin . It.ds.DataSourceAspect "/> <aop:config> <aop:aspect id=" C "ref=" Datasourceaspe CT "> <aop:pointcut id=" tx "expression=" Execution (* com.robin.it.permission.service.*.* (..)) " /> <aop:before pointcut-ref= "TX" method= "before"/> </aop:aspect> < ;/aop:config></beans>
Focus on the yellow part: How to configure a multi-data source, and when you know the relevant method (Aop:before) for the corresponding service [Com.robin.it.permission.service.*.* (..)], Execute Datasourceaspect slice method;
Database configuration file Database.properties reference, previous section.
To this, a multi-data source, read-write separation configuration has been completed.
Spring Data Source Configuration three: multiple data sources