使用springAOP方式进行数据源的切换。
细节见配置:
DataSourceAspect.java
import
java.lang.reflect.Method;
import
org.springframework.aop.AfterReturningAdvice;
import
org.springframework.aop.MethodBeforeAdvice;
import
com.stech.system.annotation.DataSource;
import
com.stech.system.config.DataSourceHolder;
public
class
DataSourceAspect
implements
MethodBeforeAdvice, AfterReturningAdvice {
@Override
public
void
afterReturning(Object
returnValue
, Method
method
, Object[]
args
, Object
target
)
throws
Throwable {
DataSourceHolder.clearDataSourceType();
}
@Override
public
void
before(Method
method
, Object[]
args
, Object
target
)
throws
Throwable {
if
(
method
.isAnnotationPresent(
DataSource
.
class
)) {
DataSource
datasource
=
method
.getAnnotation(
DataSource
.
class
);
DataSourceHolder.setDataSourceType(
datasource
.value());
}
}
}
|
DataSourceHolder.java
public
class
DataSourceHolder {
private
static
final
ThreadLocal<String>
contextHolder
=
new
ThreadLocal<String>();
/**
*
@Description
: 设置数据源类型
*
@param
dataSourceType 数据库类型
*
@return
void
*
@throws
*/
public
static
void
setDataSourceType(String
dataSourceType
) {
contextHolder
.set(
dataSourceType
);
}
/**
*
@Description
: 获取数据源类型
*
@param
*
@return
String
*
@throws
*/
public
static
String getDataSourceType() {
return
contextHolder
.get();
}
/**
*
@Description
: 清除数据源类型
*
@param
*
@return
void
*
@throws
*/
public
static
void
clearDataSourceType() {
contextHolder
.remove();
}
|
RoutingDataSource.java
import
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public
class
RoutingDataSource
extends
AbstractRoutingDataSource {
@Override
protected
Object determineCurrentLookupKey() {
return
DataSourceHolder.getDataSourceType();
}
}
|
DataSource.java
import
java.lang.annotation.ElementType;
import
java.lang.annotation.Retention;
import
java.lang.annotation.RetentionPolicy;
import
java.lang.annotation.Target;
/**
* 数据源
*
@author
jiangnan
**
@see
com.stech.system.interceptor.DataSourceAspect
*/
@Target
(ElementType.
METHOD
)
@Retention
(RetentionPolicy.
RUNTIME
)
public
@interface
DataSource
{
String value()
default
""
;
}
|
spring-config-multidatasource.xml
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
beans
xmlns
=
"http://www.springframework.org/schema/beans"
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation
=
"
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd"
>
<!-- 数据源druid的配置 -->
<
bean
id
=
"parentDataSource"
class
=
"com.alibaba.druid.pool.DruidDataSource"
init-method
=
"init"
destroy-method
=
"close"
>
<!-- 基本属性 url、user、password -->
<
property
name
=
"url"
value
=
"${connection1.url}"
/>
<
property
name
=
"username"
value
=
"${connection1.username}"
/>
<
property
name
=
"password"
value
=
"${connection1.password}"
/>
<!-- 配置初始化大小、最小、最大 -->
<
property
name
=
"initialSize"
value
=
"${druid.initialSize}"
/>
<
property
name
=
"minIdle"
value
=
"${druid.minIdle}"
/>
<
property
name
=
"maxActive"
value
=
"${druid.maxActive}"
/>
<!-- 配置获取连接等待超时的时间 -->
<
property
name
=
"maxWait"
value
=
"${druid.maxWait}"
/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<
property
name
=
"timeBetweenEvictionRunsMillis"
value
=
"${druid.timeBetweenEvictionRunsMillis}"
/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<
property
name
=
"minEvictableIdleTimeMillis"
value
=
"${druid.minEvictableIdleTimeMillis}"
/>
<
property
name
=
"validationQuery"
value
=
"${druid.validationQuery}"
/>
<
property
name
=
"testWhileIdle"
value
=
"${druid.testWhileIdle}"
/>
<
property
name
=
"testOnBorrow"
value
=
"${druid.testOnBorrow}"
/>
<
property
name
=
"testOnReturn"
value
=
"${druid.testOnReturn}"
/>
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。 -->
<
property
name
=
"poolPreparedStatements"
value
=
"${druid.poolPreparedStatements}"
/>
<
property
name
=
"maxPoolPreparedStatementPerConnectionSize"
value
=
"${druid.maxPoolPreparedStatementPerConnectionSize}"
/>
<!-- 配置监控统计拦截的filters -->
<
property
name
=
"filters"
value
=
"${druid.filters}"
/>
</
bean
>
<
bean
id
=
"dataSource1"
parent
=
"parentDataSource"
>
<
property
name
=
"url"
value
=
"${connection1.url}"
/>
<
property
name
=
"username"
value
=
"${connection1.username}"
/>
<
property
name
=
"password"
value
=
"${connection1.password}"
/>
</
bean
>
<
bean
id
=
"dataSource2"
parent
=
"parentDataSource"
>
<
property
name
=
"url"
value
=
"${connection2.url}"
/>
<
property
name
=
"username"
value
=
"${connection2.username}"
/>
<
property
name
=
"password"
value
=
"${connection2.password}"
/>
</
bean
>
<!-- method 1: config switch routing db -->
<
bean
id
=
"dataSource"
class
=
"com.stech.system.config.RoutingDataSource"
>
<
property
name
=
"targetDataSources"
>
<
map
key-type
=
"java.lang.String"
>
<
entry
key
=
"${jdbc-key-1}"
value-ref
=
"dataSource1"
/>
<
entry
key
=
"${jdbc-key-2}"
value-ref
=
"dataSource2"
/>
</
map
>
</
property
>
<
property
name
=
"defaultTargetDataSource"
ref
=
"dataSource1"
/>
</
bean
>
</
beans
>
|
spring-config-tx.xml
<?
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:tx
=
"http://www.springframework.org/schema/tx"
xmlns:context
=
"http://www.springframework.org/schema/context"
xmlns:aop
=
"http://www.springframework.org/schema/aop"
xmlns:task
=
"http://www.springframework.org/schema/task"
xsi:schemaLocation
=
"
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.1.xsd"
default-lazy-init
=
"false"
>
<!-- 声明式事务管理 -->
<
bean
id
=
"transactionManager"
class
=
"org.springframework.jdbc.datasource.DataSourceTransactionManager"
>
<
property
name
=
"dataSource"
ref
=
"dataSource"
/>
</
bean
>
<!-- 配置事务传播特性 isolation="READ_COMMITTED" -->
<
tx:advice
id
=
"userTxAdvice"
transaction-manager
=
"transactionManager"
>
<
tx:attributes
>
<
tx:method
name
=
"insert*"
propagation
=
"REQUIRED"
read-only
=
"false"
rollback-for
=
"java.lang.Exception"
/>
<
tx:method
name
=
"save*"
propagation
=
"REQUIRED"
read-only
=
"false"
rollback-for
=
"java.lang.Exception"
/>
<
tx:method
name
=
"add*"
propagation
=
"REQUIRED"
read-only
=
"false"
rollback-for
=
"java.lang.Exception"
/>
<
tx:method
name
=
"update*"
propagation
=
"REQUIRED"
read-only
=
"false"
rollback-for
=
"java.lang.Exception"
/>
<
tx:method
name
=
"delete*"
propagation
=
"REQUIRED"
read-only
=
"false"
rollback-for
=
"java.lang.Exception"
/>
<
tx:method
name
=
"remove*"
propagation
=
"REQUIRED"
read-only
=
"false"
rollback-for
=
"java.lang.Exception"
/>
<
tx:method
name
=
"*"
read-only
=
"true"
rollback-for
=
"java.lang.Exception"
/>
</
tx:attributes
>
</
tx:advice
>
<
bean
id
=
"dataSourceAspect"
class
=
"com.stech.system.interceptor.DataSourceAspect"
></
bean
>
<!-- 配置参与事务的类 -->
<
aop:config
expose-proxy
=
"true"
proxy-target-class
=
"true"
>
<
aop:pointcut
id
=
"pc"
expression
=
"execution(* com.*.*.service.*.*.*(..))"
/>
<
aop:advisor
pointcut-ref
=
"pc"
advice-ref
=
"userTxAdvice"
order
=
"2"
/>
<
aop:advisor
pointcut-ref
=
"pc"
advice-ref
=
"dataSourceAspect"
order
=
"1"
/>
</
aop:config
>
<
tx:annotation-driven
proxy-target-class
=
"true"
transaction-manager
=
"transactionManager"
/>
|
#database configuration
jdbc.type=
mysql
jdbc-key-1=
stuaffairs
connection1.url=
jdbc:mysql://localhost:3306/stuaffairs?useUnicode=true&characterEncoding=utf8
connection1.username=
root
connection1.password=
jdbc-key-2=
datacenter
connection2.url=
jdbc:mysql://localhost:3306/datacenter?useUnicode=true&characterEncoding=utf8
connection2.username=
root
connection2.password=
#druid
druid.initialSize=
10
druid.minIdle=
10
druid.maxActive=
50
druid.maxWait=
60000
druid.timeBetweenEvictionRunsMillis=
60000
druid.minEvictableIdleTimeMillis=
300000
druid.validationQuery=
SELECT
'x'
druid.testWhileIdle=
true
druid.testOnBorrow=
false
druid.testOnReturn=
false
druid.poolPreparedStatements=
true
druid.maxPoolPreparedStatementPerConnectionSize=
20
druid.filters=
wall,stat
|
测试通过。 利用在service的method上的注解,进行数据源的切换,同时增加了数据库的事务。