Spring對數據庫的鏈接配置 事務的配置

第一點 要配置數據源  數據源包括: 數據源連接信息,數據源的類支持。

配置方式有兩種 :

 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

https://blog.csdn.net/gaofuqi/article/details/46417281

猜你喜欢

转载自blog.csdn.net/qq_16773855/article/details/79743111