package com.taobao.tddl.group.dbselector; import java.sql.SQLException; import java.util.Collections; import java.util.List; import java.util.Map; import javax.sql.DataSource; import com.taobao.tddl.common.jdbc.TExceptionUtils; import com.taobao.tddl.common.jdbc.sorter.ExceptionSorter; import com.taobao.tddl.common.model.DBType; import com.taobao.tddl.group.jdbc.DataSourceWrapper; /** * 对等数据库选择器。 在数据完全相同的一组库中选择一个库 用于对HA/RAC情况,多个读库中取一个读的操作 * * @author linxuan */ public interface DBSelector { public static final int NOT_EXIST_USER_SPECIFIED_INDEX = -1; /** * @return 返回该Selector的标识 */ String getId(); /** * 对等数据库选择器。 在数据完全相同的一组库中选择一个库 用于对HA/RAC情况,多个读库中取一个读的操作 */ DataSource select(); /** * 返回指定dsKey对应的数据源。若对应数据源的当前权重为0,则返回null 这个方法同时可以用来判断一个dsKey对应的库是否可读或可写: * rselector.get(wBaseDsKey) != null 则可读 wselector.get(rBaseDsKey) != null * 则可写 TGroupConnection读写连接复用的旧实现会用到这个功能 * * @param dsKey 内部和每一个物理DataSource对应的key, 在初始化dbSelector时指定 * @return 返回dsKey对应的数据源 */ DataSource get(String dsKey); /** * 设置数据库类型:目前只用来选择exceptionSorter */ void setDbType(DBType dbType); /** * 以选择到的DataSource和传入的args,重试执行 tryer.tryOnDataSource(String dsKey, * DataSource ds, Object... args) 每次选择DataSource会排除上次重试失败的, * 直到达到指定的重试次数,或期间抛出非数据库不可用异常 抛出异常后,以历次重试异常列表,和最初的args,调用 * tryer.onSQLException(List<SQLException> exceptions, Object... args) * * @param tryer * @param times * @param args * @throws SQLException */ <T> T tryExecute(DataSourceTryer<T> tryer, int times, Object... args) throws SQLException; /** * @param failedDataSources: 在调用该方法前,已经得知试过失败的DataSource和对应的SQLException * 存在这个参数的原因,是因为数据库操作割裂为getConnection/createStatement/execute几步,而并不是在一个大的try * catch中 failedDataSources == null 表示不需要重试,遇到任何异常直接抛出。如在写库上的操作 */ <T> T tryExecute(Map<DataSource, SQLException> failedDataSources, DataSourceTryer<T> tryer, int times, Object... args) throws SQLException; /** * 是否支持重试。 这个接口是冗余接口。如果重试功能足够稳定,可以去掉。保留不需要重试的场景提供双重保证 * * @return 是否支持重试 */ boolean isSupportRetry(); void setReadable(boolean readable); Map<String, DataSource> getDataSources(); // 直接获取对应的数据源 /** * 在DBSelector管理的数据源上重试执行操作的回调接口 */ public static interface DataSourceTryer<T> { /** * @param dsKey 内部和每一个物理DataSource对应的key, 在初始化dbSelector时指定 * @param ds * @param args 用户调用tryExecute时传入的参数列表 * @return * @throws SQLException */ // T tryOnDataSource(String dsKey, DataSource ds, Object... args) throws // SQLException; /** * tryExecute中重试调用tryOnDataSource遇到非数据库不可用异常,或用完重试次数时,会调用该方法 * * @param exceptions 历次重试失败抛出的异常。 最后一个异常可能是数据库不可用的异常,也可能是普通的SQL异常 * 最后一个之前的异常是数据库不可用的异常 * @param exceptionSorter 当前用到的判断Exception类型的分类器 * @param args 与tryOnDataSource时的args相同,都是用户调用tryExecute时传入的arg * @return 用户(实现者)觉得是否返回什么值 * @throws SQLException */ T onSQLException(List<SQLException> exceptions, ExceptionSorter exceptionSorter, Object... args) throws SQLException; T tryOnDataSource(DataSourceWrapper dsw, Object... args) throws SQLException; } /** * DataSourceTryer.onSQLException 直接抛出异常 */ public static abstract class AbstractDataSourceTryer<T> implements DataSourceTryer<T> { @SuppressWarnings("unchecked") public T onSQLException(List<SQLException> exceptions, ExceptionSorter exceptionSorter, Object... args) throws SQLException { TExceptionUtils.throwSQLException(exceptions, null, Collections.EMPTY_LIST); return null; } } }