分库分表思路
标签:
| 发表时间:2015-03-09 18:24 | 作者:ahua186186
出处:http://www.iteye.com
总体思路和切入点:
1.在spring数据访问封装层侵入代码加入分库分表策略。
(1)分库:通过侵入SqlSession的代码并传入分库参数来选择sqlSessionTemplate的数据源的方式实现分库策略
(2)分表:通过侵入Configuration并传入分表参数来替换BoundSql的sql的方式实现分表策略
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐
1.在spring数据访问封装层侵入代码加入分库分表策略。
(1)分库:通过侵入SqlSession的代码并传入分库参数来选择sqlSessionTemplate的数据源的方式实现分库策略
public abstract class SqlSessionDaoSupport implements InitializingBean { private SqlSessionFactoryBean sqlSessionFactoryBean; private Map<DataSource, SqlSessionTemplate> dataSourceMap; private SqlSession sqlSession; { //通过侵入SqlSession的代码并传入分库参数来选择sqlSessionTemplate的数据源的方式实现分库策略 sqlSession = (SqlSession) Proxy.newProxyInstance(SqlSessionDaoSupport.class.getClassLoader(), new Class[] { SqlSession.class }, new SessionHandler()); } @Autowired(required = false) public final void setSqlSessionFactory(SqlSessionFactoryBean sqlSessionFactoryBean) { this.sqlSessionFactoryBean = sqlSessionFactoryBean; } public final SqlSession getSqlSession() { return sqlSession; } @Override public final void afterPropertiesSet() throws Exception { sqlSessionFactoryBean.afterPropertiesSet(); // dataSourceMap = new LinkedHashMap<DataSource, SqlSessionTemplate>(); dataSourceMap.put(sqlSessionFactoryBean.getMainDataSource(), new SqlSessionTemplate(sqlSessionFactoryBean.getMainSqlSessionFactory())); Map<String, DataSource> shardDataSources = sqlSessionFactoryBean.getShardDataSources(); if (shardDataSources != null) { for (Entry<String, DataSource> entry : shardDataSources.entrySet()) { SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getShardSqlSessionFactory().get( entry.getKey()); dataSourceMap.put(entry.getValue(), new SqlSessionTemplate(sqlSessionFactory)); } } } private class SessionHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { // 默认DataSource为MainDataSource DataSource targetDS = sqlSessionFactoryBean.getMainDataSource(); // if (args == null || args.length == 0) { // 准备事务 prepareTx(targetDS); // return method.invoke(dataSourceMap.get(sqlSessionFactoryBean.getMainDataSource()), args); } if (!(args[0] instanceof String)) { // 准备事务 prepareTx(targetDS); // return method.invoke(dataSourceMap.get(sqlSessionFactoryBean.getMainDataSource()), args); } ShardParam shardParam = (args.length > 1 && args[1] instanceof ShardParam) ? (ShardParam) args[1] : null; if (shardParam == null) { // 准备事务 prepareTx(targetDS); // return method.invoke(dataSourceMap.get(sqlSessionFactoryBean.getMainDataSource()), args); } else { args[1] = shardParam.getParams(); } // String statement; String shardStrategyName; ShardStrategy shardStrategy; statement = (String) args[0]; shardStrategyName = shardParam.getName(); shardStrategy = sqlSessionFactoryBean.getShardStrategyMap().get(shardStrategyName); if (shardStrategy == null) { shardStrategy = NoShardStrategy.INSTANCE; } Configuration configuration = sqlSessionFactoryBean.getMainSqlSessionFactory().getConfiguration(); MappedStatement mappedStatement = configuration.getMappedStatement(statement); BoundSql boundSql = mappedStatement.getBoundSql(wrapCollection(shardParam.getParams())); shardStrategy.setMainDataSource(sqlSessionFactoryBean.getMainDataSource()); shardStrategy.setShardDataSources(sqlSessionFactoryBean.getShardDataSources()); shardStrategy.setShardParam(shardParam); shardStrategy.setSql(boundSql.getSql()); // StrategyHolder.setShardStrategy(shardStrategy); // 重新指定目标DataSource targetDS = shardStrategy.getTargetDataSource(); SqlSessionTemplate sqlSessionTemplate = null; if (targetDS == null || (sqlSessionTemplate = dataSourceMap.get(targetDS)) == null) { targetDS = sqlSessionFactoryBean.getMainDataSource(); sqlSessionTemplate = dataSourceMap.get(targetDS); } // 准备事务 prepareTx(targetDS); return method.invoke(sqlSessionTemplate, args); } finally { StrategyHolder.removeShardStrategy(); } } /** * 、 准备事务 * * @param targetDS */ private void prepareTx(DataSource targetDS) { // TransactionHolder.setDataSource(targetDS); // for transaction TransactionInfoWrap txInfo = TransactionHolder.getTransactionInfo(); if (txInfo != null) { TransactionAttribute attr = txInfo.getTransactionAttribute(); if (attr != null) { createTxIfAbsent(targetDS, txInfo); } } } /** * 如果不存在则创建事务 * * @param targetDS * @param txInfo */ private void createTxIfAbsent(DataSource targetDS, TransactionInfoWrap txInfo) { Map<DataSource, LinkedList<TransactionInfoWrap>> txTree = TransactionHolder.getTxTree(); if (txTree == null || !txTree.containsKey(targetDS)) { createTx(targetDS, txInfo); } } private void createTx(DataSource targetDS, TransactionInfoWrap txInfo) { TransactionStatus txStatus = txInfo.getTransactionManager() .getTransaction(txInfo.getTransactionAttribute()); // txStatus = new TransactionStatusWrap((DefaultTransactionStatus) // txStatus); TransactionHolder.addStatusDS(txStatus, targetDS); // TransactionInfoWrap txInfoCopy = txInfo.newCopy(); txInfoCopy.newTransactionStatus(txStatus); // TransactionHolder.addTxInfo2Tree(targetDS, txInfoCopy); } private Object wrapCollection(final Object object) { if (object instanceof List) { return new HashMap<String, Object>() { private static final long serialVersionUID = -2533602760878803345L; { put("list", object); } }; } else if (object != null && object.getClass().isArray()) { return new HashMap<String, Object>() { private static final long serialVersionUID = 8371167260656531195L; { put("array", object); } }; } return object; } } }
(2)分表:通过侵入Configuration并传入分表参数来替换BoundSql的sql的方式实现分表策略
public class SqlSessionFactoryBean implements ApplicationContextAware, MultiDataSourceSupport { private final Logger logger = LoggerFactory.getLogger(getClass()); private ApplicationContext applicationContext; private DataSource mainDataSource; private SqlSessionFactory mainSqlSessionFactory; private Map<String, DataSource> shardDataSources; private Map<String, SqlSessionFactory> shardSqlSessionFactory; private List<DataSource> shardDataSourceList; private Resource[] mapperLocations; private Map<String, ShardStrategy> shardStrategyMap = new HashMap<String, ShardStrategy>(); private Map<String, Class<?>> shardStrategyConfig = new HashMap<String, Class<?>>(); private SqlConverter sqlConverter = new DefaultSqlConverter(); public DataSource getMainDataSource() { return mainDataSource; } public void setMainDataSource(DataSource mainDataSource) { if (mainDataSource instanceof TransactionAwareDataSourceProxy) { // If we got a TransactionAwareDataSourceProxy, we need to perform // transactions for its underlying target DataSource, else data // access code won't see properly exposed transactions (i.e. // transactions for the target DataSource). this.mainDataSource = ((TransactionAwareDataSourceProxy) mainDataSource).getTargetDataSource(); } else { this.mainDataSource = mainDataSource; } } public void setShardDataSourceList(List<DataSource> shardDataSourceList) { this.shardDataSourceList = shardDataSourceList; } public Map<String, DataSource> getShardDataSources() { return shardDataSources; } public void setMapperLocations(Resource[] mapperLocations) { this.mapperLocations = mapperLocations; } public void setShardStrategy(Map<String, Class<?>> shardStrategyMap) { this.shardStrategyConfig = shardStrategyMap; } public SqlSessionFactory getMainSqlSessionFactory() { return mainSqlSessionFactory; } public Map<String, SqlSessionFactory> getShardSqlSessionFactory() { return shardSqlSessionFactory; } public Map<String, ShardStrategy> getShardStrategyMap() { return shardStrategyMap; } public void afterPropertiesSet() throws Exception { if (mainDataSource == null && (shardDataSourceList == null || shardDataSourceList.size() == 0)) { throw new RuntimeException( " Property 'mainDataSource' and property 'shardDataSourceList' can not be null together! "); } if (shardDataSourceList != null && shardDataSourceList.size() > 0) { shardDataSources = new LinkedHashMap<String, DataSource>(); Map<String, DataSource> dataSourceMap = applicationContext.getBeansOfType(DataSource.class); for (Entry<String, DataSource> entry : dataSourceMap.entrySet()) { for (int i = 0; i < shardDataSourceList.size(); i++) { DataSource ds = shardDataSourceList.get(i); if (entry.getValue() == ds) { DataSource dataSource = entry.getValue(); if (dataSource instanceof TransactionAwareDataSourceProxy) { dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource(); } shardDataSources.put(entry.getKey(), dataSource); } } } } if (mainDataSource == null) { if (shardDataSourceList.get(0) instanceof TransactionAwareDataSourceProxy) { this.mainDataSource = ((TransactionAwareDataSourceProxy) shardDataSourceList.get(0)) .getTargetDataSource(); } else { mainDataSource = shardDataSources.get(0); } } this.mainSqlSessionFactory = buildSqlSessionFactory(getMainDataSource()); if (getShardDataSources() != null && getShardDataSources().size() > 0) { shardSqlSessionFactory = new LinkedHashMap<String, SqlSessionFactory>(getShardDataSources().size()); for (Entry<String, DataSource> entry : getShardDataSources().entrySet()) { shardSqlSessionFactory.put(entry.getKey(), buildSqlSessionFactory(entry.getValue())); } } // if (shardStrategyConfig != null) { shardStrategyMap = new HashMap<String, ShardStrategy>(); for (Map.Entry<String, Class<?>> entry : shardStrategyConfig.entrySet()) { Class<?> clazz = entry.getValue(); if (!ShardStrategy.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException("class " + clazz.getName() + " is illegal, subclass of ShardStrategy is required."); } try { shardStrategyMap.put(entry.getKey(), (ShardStrategy) (entry.getValue().newInstance())); } catch (Exception e) { throw new RuntimeException("new instance for class " + clazz.getName() + " failed, error:" + e.getMessage()); } } // shardStrategyConfig = null; } } private SqlSessionFactory buildSqlSessionFactory(DataSource dataSource) throws IOException { ShardPlugin plugin = new ShardPlugin(); plugin.setSqlConverter(sqlConverter); Configuration configuration = null; SpringManagedTransactionFactory transactionFactory = null; // configuration = new Configuration(); configuration.addInterceptor(plugin);//通过侵入Configuration并传入分表参数来替换BoundSql的sql的方式实现分表策略 // transactionFactory = new SpringManagedTransactionFactory(dataSource); Environment environment = new Environment(SqlSessionFactoryBean.class.getSimpleName(), transactionFactory, dataSource); configuration.setEnvironment(environment); if (!ObjectUtils.isEmpty(this.mapperLocations)) { for (Resource mapperLocation : this.mapperLocations) { if (mapperLocation == null) { continue; } // this block is a workaround for issue // http://code.google.com/p/mybatis/issues/detail?id=235 // when running MyBatis 3.0.4. But not always works. // Not needed in 3.0.5 and above. String path; if (mapperLocation instanceof ClassPathResource) { path = ((ClassPathResource) mapperLocation).getPath(); } else { path = mapperLocation.toString(); } try { XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, path, configuration.getSqlFragments()); xmlMapperBuilder.parse(); } catch (Exception e) { throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e); } finally { ErrorContext.instance().reset(); } if (this.logger.isDebugEnabled()) { this.logger.debug("Parsed mapper file: '" + mapperLocation + "'"); } } } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Property 'mapperLocations' was not specified or no matching resources found"); } } return new SqlSessionFactoryBuilder().build(configuration); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
已有 0 人发表留言,猛击->> 这里<<-参与讨论
ITeye推荐