在數據庫層面大都采用讀寫分離技術,就是一個Master數據庫,多個Slave數據庫。Master庫負責數據更新和實時數據查詢,Slave庫當然負責非實時數據查詢。因為在實際的應用中,數據庫都是讀多寫少(讀取數據的頻率高,更新數據的頻率相對較少),而讀取數據通常耗時比較長,占用數據庫服務器的CPU較多,從而影響用戶體驗。我們通常的做法就是把查詢從主庫中抽取出來,采用多個從庫,使用負載均衡,減輕每個從庫的查詢壓力。
廢話不多說,多數據源配置和主從數據配置原理一樣
1、首先配置 jdbc.properties 兩個數據庫 A 和 B
1
2
3
4
5
6
7
8
9
10
11
|
#============ 雙數據源 ======# #----------------------A servers--------------------------# A.driver=com.mysql.jdbc.Driver A.url=jdbc:mysql: //localhost:3619/gps4?useUnicode=true&characterEncoding=utf8 A.username=gpsadmin A.password=1qaz& 619 #----------------------B servers--------------------------# B.driver=com.mysql.jdbc.Driver B.url=jdbc:mysql: //localhost:3619/gps6?useUnicode=true&characterEncoding=utf8 B.username=gpsadmin B.password=1qaz& 619 |
2、配置 spring-mybatis.xml 文件【重要】
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
<!-- 引入配置文件 --> <bean id= "propertyConfigurer" class = "org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" > <property name= "location" value= "classpath:resources/jdbc.properties" /> </bean> <!-- DBCP連接池 --> <!-- <bean id= "dataSource" class = "org.apache.commons.dbcp.BasicDataSource" destroy-method= "close" > <property name= "driverClassName" value= "${driver}" /> <property name= "url" value= "${url}" /> <property name= "username" value= "${username}" /> <property name= "password" value= "${password}" /> 初始化連接大小 <property name= "initialSize" value= "${initialSize}" ></property> 連接池最大數量 <property name= "maxActive" value= "${maxActive}" ></property> 連接池最大空閑 <property name= "maxIdle" value= "${maxIdle}" ></property> 連接池最小空閑 <property name= "minIdle" value= "${minIdle}" ></property> 獲取連接最大等待時間 <property name= "maxWait" value= "${maxWait}" ></property> </``> --> <!-- 【重點】 A 數據源 --> <bean name= "dataSourceA" class = "org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name= "driverClassName" value= "${A.driver}" /> <property name= "url" value= "${A.url}" /> <property name= "username" value= "${A.username}" /> <property name= "password" value= "${A.password}" /> </bean> <!-- 【重點】 B 數據源 --> <bean name= "dataSourceB" class = "org.springframework.jdbc.datasource.DriverManagerDataSource" > <property name= "driverClassName" value= "${B.driver}" /> <property name= "url" value= "${B.url}" /> <property name= "username" value= "${B.username}" /> <property name= "password" value= "${B.password}" /> </bean> <!--【重點】 雙數據源 配合 --> <bean id= "dataSource" class = "com.ifengSearch.common.database.DynamicDataSource" > <property name= "defaultTargetDataSource" ref= "dataSourceB" /> <property name= "targetDataSources" > <map> <entry key= "dataSourceA" value-ref= "dataSourceA" /> <entry key= "dataSourceB" value-ref= "dataSourceB" /> </map> </property> </bean> <!-- 【重點】 加入 aop 自動掃描 DataSourceAspect 配置數據庫注解aop --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id= "manyDataSourceAspect" class = "com.ifengSearch.common.database.DataSourceAspect" /> <aop:config> <!-- 掃描 注解的 數據源 --> <aop:aspect id= "c" ref= "manyDataSourceAspect" > <aop:pointcut id= "tx" expression= "execution(* com.ifengSearch.*.dao.*.*(..))" /> <aop:before pointcut-ref= "tx" method= "before" /> </aop:aspect> </aop:config> <!-- 配置數據連接 工廠 自動掃描mapping.xml文件 --> <bean id= "sqlSessionFactory" class = "org.mybatis.spring.SqlSessionFactoryBean" > <property name= "dataSource" ref= "dataSource" /> <!-- 自動掃描mapping.xml文件 --> <property name= "mapperLocations" value= "classpath:com/ifengSearch/*/mapping/*.xml" ></property> </bean> <!-- DAO接口所在包名,Spring會自動查找其下的類 --> <bean class = "org.mybatis.spring.mapper.MapperScannerConfigurer" > <property name= "basePackage" value= "com.ifengSearch.*.dao" /> <property name= "sqlSessionFactoryBeanName" value= "sqlSessionFactory" ></property> </bean> <!-- (事務管理)transaction manager, use JtaTransactionManager for global tx 事務 --> <bean id= "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > <property name= "dataSource" ref= "dataSource" /> </bean> |
3、編寫幾個java類動態調用數據源【重要】
a:自定義一個注解,負責動態調用數據源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.ifengSearch.common.database; import java.lang.annotation.*; /** * 設置 數據源 注解標簽的用法 寫上注解標簽, * 調用相應方法切換數據源咯(就跟你設置事務一樣) * 【也可以配置 主從數據庫】 * * @author flm * @2017年9月12日 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface DataSource { public static String dataSourceA = "dataSourceA" ; // A數據源 public static String dataSourceB = "dataSourceB" ; // B數據源 String value(); } |
b、數據源的獲取 Object aop實現 (反射)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
package com.ifengSearch.common.database; import java.lang.reflect.Method; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; /** * 數據源的獲取 * aop實現 (反射) * @author flm * @2017年9月12日 */ public class DataSourceAspect{ private Logger log = Logger.getLogger(DataSourceAspect.class); public void before(JoinPoint point) { Object target = point.getTarget(); // 攔截的實體類 String method = point.getSignature().getName(); // 攔截的方法名稱 Class<?>[] classz = target.getClass().getInterfaces(); Class<?>[] parameterTypes = ((MethodSignature) point.getSignature()) .getMethod().getParameterTypes(); // 攔截的方法參數類型 try { Method m = classz[0].getMethod(method, parameterTypes); if (m != null && m.isAnnotationPresent(DataSource.class)) { DataSource data = m .getAnnotation(DataSource.class); DataSourceHolder.setDataSource(data.value()); log.info( "數據源的獲取 DataSource: " +data.value()); } } catch (Exception e) { log.error( "數據源的獲取 aop實現 出錯:" +e.getMessage()); } } } |
c、DataSourceHolder 數據源操作 獲取數據源 幫助類
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
package com.ifengSearch.common.database; /** * 多數據源 * 數據源操作 獲取數據源 * @author flm * @2017年9月12日 */ public class DataSourceHolder { //線程本地環境 private static final ThreadLocal<String> dataSources = new ThreadLocal<String>(); //設置數據源 public static void setDataSource(String customerType) { dataSources.set(customerType); } //獲取數據源 public static String getDataSource() { return (String) dataSources.get(); } //清除數據源 public static void clearDataSource() { dataSources.remove(); } } |
d、 我們還需要實現spring的抽象類AbstractRoutingDataSource,就是實現determineCurrentLookupKey方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.ifengSearch.common.database; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 多數據源 * 獲取數據源(依賴于spring) * @author flm * @2017年9月12日 */ public class DynamicDataSource extends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey() { return DataSourceHolder.getDataSource(); } } |
4、接下來就可以看結果了
我在dao層直接調用
1
2
3
4
5
6
7
8
9
10
11
12
|
public interface UserDao { /** * 登錄判斷 【數據源B】 */ @DataSource(value=DataSource.dataSourceB) public List<UserBean> getLoginUserList(@Param( "loginName" )String loginName,@Param( "loginPwd" )String loginPwd); /** * 查找上一級 服務商 【數據源A】 */ @DataSource(value=DataSource.dataSourceA) public UserBean getServerUser(@Param( "u_last_id" )Integer u_last_id); } |
總結
以上所述是小編給大家介紹的Spring+Mybatis 實現aop數據庫讀寫分離與多數據庫源配置操作,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!
原文鏈接:http://www.cnblogs.com/lemon-flm/p/7510774.html