在数据库层面大都采用读写分离技术,就是一个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