2
回答
多数据源动态切换不能正常切换
利用AWS快速构建适用于生产的无服务器应用程序,免费试用12个月>>>   

在开发双数据源按需切换的时候,发现不能按要求切换数据源。环境是:struts2、spring、Hibernate。源代码如下:

数据源设置类:

public class DataSourceSwitcher {
	
	public static final String DATA_SOURCE_MASTER = "master"; // 主库
    public static final String DATA_SOURCE_SLAVE = "slave"; // 从库
	
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  

    /**
     * 设置数据源
     */
    public static void setDataSource(String dbName) {
        contextHolder.set(dbName);
    }
    
    /**
     * 获取数据源
     */
    public static String getDataSource() {
        return (String) contextHolder.get();
    }
    
    /**
     * 删除数据源
     */
    public static void removeDataSource() {
        contextHolder.remove();
    } 
}

动态切换类:

public class DynamicDataSource extends AbstractRoutingDataSource {
	
	@Override
	protected Object determineCurrentLookupKey() {
		System.out.println("切换数据源到:" + DataSourceSwitcher.getDataSource());
		return DataSourceSwitcher.getDataSource();
	}

	@Override
	public Logger getParentLogger() throws SQLFeatureNotSupportedException {
		return null;
	}
}

AOP切面类:

@Component
@Aspect
@Order(0)
public class DataSourceAdvice {
	
	private static Logger logger = Logger.getLogger(DataSourceAdvice.class);
	
	@Before(("@annotation(com.common.DynamicDataSourceAnnotation)"))
	public void before(JoinPoint point) {
		try {
	        // 获取数据源
			String dataSource = getDataSource(point);
			
	        // 设置数据源
	        logger.info("设置数据源" + dataSource);
	        DataSourceSwitcher.setDataSource(dataSource);
        } catch (Exception e) {
        	logger.error("设置数据源失败...");
        	logger.error(StringHandleUtils.getExceptionInfo(e));
        }
	}
	

	@After("@annotation(com.common.DynamicDataSourceAnnotation)")   //后置通知
    public void testAfter(JoinPoint point){
		try {
	        // 获取数据源
			String dataSource = getDataSource(point);
			
			// 若数据源不是主库,则清空
			if(!DataSourceSwitcher.DATA_SOURCE_MASTER.equals(dataSource)) {
				logger.error("删除数据源" + dataSource + "成功...");
				DataSourceSwitcher.removeDataSource();
			}
        } catch (Exception e) {
        	logger.error("删除数据源失败...");
        	logger.error(StringHandleUtils.getExceptionInfo(e));
        }
    }
	
	@SuppressWarnings("rawtypes")
	private String getDataSource(JoinPoint point) {
		String dataSource = DataSourceSwitcher.DATA_SOURCE_MASTER;
		try{
			Class<?> className = point.getTarget().getClass();
	        String methodName = point.getSignature().getName();
	        Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
	        Method method = className.getMethod(methodName, argClass);
	        if (method.isAnnotationPresent(DynamicDataSourceAnnotation.class)) {
	            DynamicDataSourceAnnotation annotation = method.getAnnotation(DynamicDataSourceAnnotation.class);
	            dataSource = annotation.dataSource();
	        }
		}catch(Exception e) {
			logger.error("获取数据源失败...");
			logger.error(StringHandleUtils.getExceptionInfo(e));
		}
		
		return dataSource;
	}
}

 

注解类:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DynamicDataSourceAnnotation {
	 String dataSource() default DataSourceSwitcher.DATA_SOURCE_MASTER;
}

配置文件:

    <!-- master -->
	<bean id="master" class="com.common.DataSource">
         <property name="driver" value="${master.datasource.driverClassName}" /> 
         <property name="driverUrl" value="${master.datasource.url}" /> 
         <property name="user" value="${master.datasource.username}"/> 
         <property name="password" value="${master.datasource.password}"/> 
         <property name="alias" value="master" />
     </bean>
     
	<!-- slave -->
	<bean id="slave" class="com.common.DataSource">
	    <property name="driver" value="${slave.datasource.driverClassName}" /> 
	    <property name="driverUrl" value="${slave.datasource.url}" /> 
	    <property name="user" value="${slave.datasource.username}"/> 
	    <property name="password" value="${slave.datasource.password}"/> 
	    <property name="alias" value="slave" />
    </bean>

    <!-- 配置动态切换数据源 -->
	<bean id="dataSource" class="com.common.DynamicDataSource">  
        <property name="defaultTargetDataSource" ref="master"></property>
        <property name="targetDataSources">  
            <map key-type="java.lang.Object">  
                <entry value-ref="master" key="master"></entry>  
                <entry value-ref="slave" key="slave"></entry>
            </map>  
        </property>  
   	</bean>

    <bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>  
			</props>
		</property>
		<property name="packagesToScan" value="com.common.pojo"/>
	</bean>

    <!-- 定义事务管理器(声明式的事务)   -->
	<bean id="transactionManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

    <aop:aspectj-autoproxy proxy-target-class="true" />
	<tx:annotation-driven transaction-manager="transactionManager" />

使用:

Action层:

@Action(name = "common")
@Component
public class CommonAction extends ActionSupport{

    @Autowired
	private CommonService service;

    public String queryInfo() {
        List<TradeInfo> list = service.queryInfo();
        for(TradeInfo ti : list) {
            System.out.println("service.queryInfo():" + ti.getName());
        }
        
        System.out.println("================================");
        
        List<TradeInfo> list2 = service.queryInfo2();
        for(TradeInfo ti : list2) {
            System.out.println("service.queryInfo2():" + ti.getName());
        }
        
        return "index";
    }
}

Service层:

@Service
@Transactional(rollbackFor = Exception.class)
public class CommonService {

    @Autowired
	private CommonDAO commonDAO;

    @SuppressWarnings("unchecked")
	@Override
	@DynamicDataSourceAnnotation(dataSource = DataSourceSwitcher.DATA_SOURCE_MASTER)
	public List<TradeInfo> queryInfo() throws SystemException {
		try {
            String sql = "FROM TradeInfo";
			return this.commonDAO.find(sql);
		} catch (Exception e) {

		}
	}

    @SuppressWarnings("unchecked")
	@Override
	@DynamicDataSourceAnnotation(dataSource = DataSourceSwitcher.DATA_SOURCE_SLAVE)
	public List<TradeInfo> queryInfo2() throws SystemException {
		try {
            String sql = "FROM TradeInfo";
			return this.commonDAO.find(sql);
		} catch (Exception e) {

		}
	}
}

Dao层:

@Repository
public class CommonDao extends HibernateDaoSupport {

    public List find(String sql) throws SystemException {
		try {
			return getHibernateTemplate().find(sql);
		} catch (DataAccessException e) {

		} catch (HibernateException e) {

		} catch (SQLException e) {

		}
		
		return null;
	}
}

 

测试输出结果:

设置数据源master
切换数据源到:master
删除数据源master成功...
service.queryInfo():主数据源
================================
设置数据源slave
切换数据源到:slave
service.queryInfo()2:主数据源

从输出信息可以看出数据源并没有成功切换。queryInfo()走的是主数据源;queryInfo()2走的也是主数据源。

调试结果是:第一个方法可以正常切换数据源,第一个方法之后的方法都无法正常切换,获取的连接是第一个方法的连接。

 

而在Action中用Main方法:

public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    CommonAction = (CommonAction ) ctx.getBean("commonAction");
    c.queryInfo();
)

输出结果:

设置数据源master
切换数据源到:master
切换数据源到:master
删除数据源master成功...
service.queryInfo():主数据源
================================
设置数据源slave
切换数据源到:slave
切换数据源到:slave
service.queryInfo()2:从数据源

从输出信息可以看出数据源按照预期正常切换了。

请问出现这种问题有哪些可能因素影响呢?

 

举报
心毅
发帖于9个月前 2回/254阅
顶部