在开发双数据源按需切换的时候,发现不能按要求切换数据源。环境是: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:从数据源
从输出信息可以看出数据源按照预期正常切换了。
请问出现这种问题有哪些可能因素影响呢?