第一點 要配置數據源 數據源包括: 數據源連接信息,數據源的類支持。
配置方式有兩種 :
1 直接在xml中配置:
<bean id="testDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db1.jdbc.url}" />
<property name="username" value="${db.login.name}"></property>
<property name="password" value="${db.login.password}" />
<property name="filters" value="${db.filters}"></property>
<property name="maxActive" value="${db.pool.maxActive}"></property>
<property name="initialSize" value="${db.pool.initialSize}"></property>
<property name="minIdle" value="${db.pool.minIdle}"></property>
<property name="maxWait" value="${db.maxWait}"></property>
<property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}"></property>
<property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"></property>
<property name="validationQuery" value="${db.validationQuery}"></property>
<property name="testWhileIdle" value="${db.testWhileIdle}"></property>
<property name="testOnBorrow" value="${db.testOnBorrow}"></property>
<property name="testOnReturn" value="${db.testOnReturn}"></property>
<property name="poolPreparedStatements" value="${db.poolPreparedStatements}"></property>
<property name="maxOpenPreparedStatements" value="${db.maxOpenPreparedStatements}"></property>
<!-- 监控数据库 -->
<property name="proxyFilters">
<list>
<ref bean="log-filter" />
</list>
</property>
</bean>
2、在類文件中實現datasource的支持類,并設置數據源鏈接信息。
package common.jdbc.datasource;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import play.Play;
import com.jolbox.bonecp.BoneCPDataSource;
public class BoneCPDataSourceFactoryBean implements FactoryBean<BoneCPDataSource>, InitializingBean{
private BoneCPDataSource boneCPDataSource;
public final static int DEFAULT_POOL_MAX_SIZE = 30;
public final static int DEFAULT_POOL_MIN_SIZE = 10;
@Override
public BoneCPDataSource getObject() throws Exception {
return boneCPDataSource;
}
@Override
public Class<?> getObjectType() {
return BoneCPDataSource.class;
}
@Override
public boolean isSingleton() {
return true;
}
@Override
public void afterPropertiesSet() throws Exception {
boneCPDataSource = new BoneCPDataSource();
boneCPDataSource.setJdbcUrl(Play.configuration.getProperty("db.url"));
boneCPDataSource.setUsername(Play.configuration.getProperty("db.user"));
boneCPDataSource.setPassword(Play.configuration.getProperty("db.pass"));
boneCPDataSource.setDriverClass(Play.configuration.getProperty("db.driver"));
boneCPDataSource.setMaxConnectionsPerPartition(getIntValue("db.pool.maxSize", DEFAULT_POOL_MAX_SIZE));
boneCPDataSource.setMinConnectionsPerPartition(getIntValue("db.pool.minSize", DEFAULT_POOL_MIN_SIZE));
}
public int getIntValue(String config, int defalutValue){
String value = Play.configuration.getProperty(config );
if(!StringUtils.isEmpty(value)){
try{
defalutValue = Integer.parseInt(value);
}catch (Exception e) {
}
}
return defalutValue;
}
}
在配置完數據源之後 。可以通過Spring。jdbc的包提供的鏈接類,調用getJdbcTemplate()方法和rowmapper,執行給出的sql語句
@Repository("testDao")
public class TestDao extends JdbcDaoSupport{
public List<TestModel> findTest1(){
String sql = "select name, age from test1";
List<TestModel> list = this.getJdbcTemplate().query(sql, new RowMapper<TestModel>() {
@Override
public TestModel mapRow(ResultSet arg0, int arg1)
throws SQLException {
TestModel model = new TestModel();
model.setName(arg0.getString("name"));
model.setAge(arg0.getInt("age"));
return model;
}
});
return list;
}
如果使用第三方的持久層中間件,比如mybatis ,則將datasource制定給sqlsessionfactory。
第二點 spring事物的配置,從啓動的順序上來講,
首先:開啓事務,需要指定事務管理者
<tx:annotation-driven transaction-manager="txManager" />
事務管理者 需要制定事務管理的類 以及 作用數據源
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
此外 可以對事務管理者 提供事務的傳播屬性配置。
定義事務傳播屬性 可以通過注解的方式 也可以通過配置文件
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 对add、update、delete的方法进行事务-->
<tx:method name="*InTrasaction" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<!-- 对剩余的方法进行事务-->
<tx:method name="*" propagation="NOT_SUPPORTED"
read-only="true" />
</tx:attributes>
</tx:advice>
———————————————————分割线 动态配置数据源及选择 原理——————————————————
核心是 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
这个类实现了 对数据源的动态管理 不在详细解读源码,只说实现原理。
AbstractRoutingDataSource 有两个属性 targetDataSources和defaultTargetDataSource 用来存放动态数据源信息,和默认数据源信息。
在AbstractRoutingDataSource执行connect之前 会通过 lookupKey 获取 targetDataSource获取要链接的数据源,
所以我们要实现AbstractRoutingDataSource的获取lookupkey的方法。
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSourceType();
}
}
为了线程的安全 我们需要一个线程安全的map来存取当前方法需要的 lookupkey。
在需要切换数据源的时候 将需要的lookupkey存入map中。
如果需要用到事务,那么数据源的切换一定要放在事务开始之前。
这里提供一种解决方案:
1在需要切换数据源的方法上使用注解方式配置数据源的lookupkey信息。
2配置AOP 通过注解切换数据源。
2保证数据源的AOP优先级,保证数据源的切换在事务开启之前。
具体配置如下:
<!-- 数据源配置1 -->
<bean id="testDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db1.jdbc.url}" />
<property name="username" value="${db.login.name}"></property>
<property name="password" value="${db.login.password}" />
<property name="filters" value="${db.filters}"></property>
<property name="maxActive" value="${db.pool.maxActive}"></property>
<property name="initialSize" value="${db.pool.initialSize}"></property>
<property name="minIdle" value="${db.pool.minIdle}"></property>
<property name="maxWait" value="${db.maxWait}"></property>
<property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}"></property>
<property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"></property>
<property name="validationQuery" value="${db.validationQuery}"></property>
<property name="testWhileIdle" value="${db.testWhileIdle}"></property>
<property name="testOnBorrow" value="${db.testOnBorrow}"></property>
<property name="testOnReturn" value="${db.testOnReturn}"></property>
<property name="poolPreparedStatements" value="${db.poolPreparedStatements}"></property>
<property name="maxOpenPreparedStatements" value="${db.maxOpenPreparedStatements}"></property>
<!-- 监控数据库 -->
<property name="proxyFilters">
<list>
<ref bean="log-filter" />
</list>
</property>
</bean>
<!-- 数据源配置2 -->
<bean id="testDataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="driverClassName" value="${db.driver}" />
<property name="url" value="${db2.jdbc.url}" />
<property name="username" value="${db.login.name}"></property>
<property name="password" value="${db.login.password}" />
<property name="filters" value="${db.filters}"></property>
<property name="maxActive" value="${db.pool.maxActive}"></property>
<property name="initialSize" value="${db.pool.initialSize}"></property>
<property name="minIdle" value="${db.pool.minIdle}"></property>
<property name="maxWait" value="${db.maxWait}"></property>
<property name="timeBetweenEvictionRunsMillis" value="${db.timeBetweenEvictionRunsMillis}"></property>
<property name="minEvictableIdleTimeMillis" value="${db.minEvictableIdleTimeMillis}"></property>
<property name="validationQuery" value="${db.validationQuery}"></property>
<property name="testWhileIdle" value="${db.testWhileIdle}"></property>
<property name="testOnBorrow" value="${db.testOnBorrow}"></property>
<property name="testOnReturn" value="${db.testOnReturn}"></property>
<property name="poolPreparedStatements" value="${db.poolPreparedStatements}"></property>
<property name="maxOpenPreparedStatements" value="${db.maxOpenPreparedStatements}"></property>
<!-- 监控数据库 -->
<property name="proxyFilters">
<list>
<ref bean="log-filter" />
</list>
</property>
</bean>
<!-- 编写spring 配置文件的配置多数源映射关系 -->
<bean class="com.test.DynamicDataSource" id="dataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="testDataSource1" key="testDataSource1"></entry>
<entry value-ref="testDataSource2" key="testDataSource2"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="testDataSource1">
</property>
</bean>
<!-- 输出可执行的SQL-->
<bean id="log-filter" class="com.alibaba.druid.filter.logging.Log4jFilter">
<property name="statementExecutableSqlLogEnable" value="true" />
</bean>
<bean id="dataSourceExchange" class="com.test.DataSourceAspect"/>
<!-- 事务begin -->
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<!-- 事务end -->
<!-- 定义事务传播属性 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 对add、update、delete的方法进行事务-->
<tx:method name="*InTrasaction" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception" />
<!-- 对剩余的方法进行事务-->
<tx:method name="*" propagation="NOT_SUPPORTED"
read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="transactionPointCut" expression="execution(* com.test.dao.*.*(..))" />
<aop:advisor pointcut-ref="transactionPointCut"
advice-ref="txAdvice" order="2" />
<aop:advisor advice-ref="dataSourceExchange" pointcut-ref="transactionPointCut" order="1"/>
</aop:config>
<bean id="applicationContext" class="com.test.SpringApplicationContext"/>
<context:component-scan base-package="com.test.*"/>
</beans>
注解数据源
public class DataSourceAspect implements MethodBeforeAdvice,AfterReturningAdvice
{
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
// TODO Auto-generated method stub
DataSourceContextHolder.clearDataSourceType();
}
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
/*
判断注解类型,获取数据源信息
*/
if (method.isAnnotationPresent(DataSource.class))
{
DataSource datasource = method.getAnnotation(DataSource.class);
DataSourceContextHolder.setDataSourceType(datasource.name());
}
else
{
DataSourceContextHolder.setDataSourceType("testDataSource1");
}
}
}
测试dao
@DataSource(name=DataSource.testDataSource1)
public List<TestModel> findTest1(){
//
//
}
参考文档 动态数据源实现:https://blog.csdn.net/u011463444/article/details/72842500