1. 程式人生 > >Springboot使用sharding進行分庫分表

Springboot使用sharding進行分庫分表

pom.xml檔案加入sharing引用和druid引用

<dependency>
            <groupId>com.dangdang</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>1.5.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.12</version>
        </dependency>

application-data.properties中配置資料庫資訊

spring.jpa.show-sql=true
spring.jpa.generate-ddl=false

other.datasource.driver-class-name = com.mysql.jdbc.Driver
other.datasource.url= jdbc:mysql://192.168.12.12:3306/business?useUnicode=yes&characterEncoding=UTF-8&useSSL=false
other.datasource.username = root
other.datasource.password = root

TradeStationYear.datasource.driver-class-name = com.mysql.jdbc.Driver
TradeStationYear.datasource.url= jdbc:mysql://192.168.12.12:3306/suqirong?useUnicode=yes&characterEncoding=UTF-8&useSSL=false
TradeStationYear.datasource.username = root
TradeStationYear.datasource.password = root
TradeStationYear.tables = yl_2_trade_2017,yl_2_trade_2018

在application.properties中加data的配置

spring.profiles.active=data

建domain

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import io.swagger.annotations.ApiModelProperty;

@Entity
@Table(name = "trade_station_year”)
public class TradeStationYear implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 6891164356262972567L;

    @Id
    @Column(name = "trade_sn", length = 24)
    @ApiModelProperty(value = "裝置交易流水號")
    private String tradeSN;

    @Column(name = "card_id", length = 20)
    @ApiModelProperty(value = "卡號")
    private String cardId;

    @Column(name = "trade_address", length = 10)
    @ApiModelProperty(value = "運營點")
    private String stationAddress;

    @Column(name = "trade_date")
    @ApiModelProperty(value = "交易時間")
    private Date tradeDate;

    public TradeStationYear() {
        super();
    }

    public String getTradeSN() {
        return tradeSN;
    }

    public void setTradeSN(String tradeSN) {
        this.tradeSN = tradeSN;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public String getStationAddress() {
        return stationAddress;
    }

    public void setStationAddress(String stationAddress) {
        this.stationAddress = stationAddress;
    }

    public Date getTradeDate() {
        return tradeDate;
    }

    public void setTradeDate(Date tradeDate) {
        this.tradeDate = tradeDate;
    }

}
 

repository類

public interface DataEntryRepositoryStationYear extends JpaRepository<TradeStationYear, Long> {

    Page<TradeStationYear> findAll(Specification<TradeStationYear> specification, Pageable pageable);

 }

分庫分表的配置

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(entityManagerFactoryRef = TSSUtil.ENTITY_MANAGER_FACTORY_1, transactionManagerRef = TSSUtil.TRANSACTION_MANAGER_1, basePackages = {
        TSSUtil.BASE_PACKAGES_1 })
public class DataSourceConfigTradeStationYear {

    // 資料庫配置
    @Value("${" + TSSUtil.LABEL_1 + ".datasource.driver-class-name}")
    private String driver;
    @Value("${" + TSSUtil.LABEL_1 + ".datasource.url}")
    private String url;
    @Value("${" + TSSUtil.LABEL_1 + ".datasource.username}")
    private String username;
    @Value("${" + TSSUtil.LABEL_1 + ".datasource.password}")
    private String password;
    @Value("${" + TSSUtil.LABEL_1 + ".tables}")
    private String tables;// 分表名列表,用英文逗號分隔

    @Autowired
    private JpaProperties jpaProperties;

    @Bean(TSSUtil.TRANSACTION_MANAGER_1)
    public PlatformTransactionManager otherTransactionManager(
            @Qualifier(TSSUtil.ENTITY_MANAGER_FACTORY_1) EntityManagerFactory otherEntityManagerFactory) {
        return new JpaTransactionManager(otherEntityManagerFactory);
    }

    @Bean(TSSUtil.ENTITY_MANAGER_FACTORY_1)
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder builder,
            @Qualifier(TSSUtil.DATA_SOURCE_1) DataSource dataSource) {
        return builder.dataSource(dataSource).packages(TSSUtil.PACKAGES_1)
                .properties(jpaProperties.getHibernateProperties(dataSource)).persistenceUnit(TSSUtil.LOGICAL_TABLE_1)
                .build();
    }

    @Bean(TSSUtil.DATA_SOURCE_1)
    public DataSource getDataSource() {

     // 設定分庫對映
        Map<String, DataSource> dataSourceMap = new HashMap<>(2);
        dataSourceMap.put("ds_0", createDataSource());

        //如果是兩個資料庫

        /**

        dataSourceMap.put("ds_0", createDataSource());
        dataSourceMap.put("ds_1", createDataSource();

       */

        //設定預設資料庫
        DataSourceRule dataSourceRule = new DataSourceRule(dataSourceMap);

        // 設定分表對映
        TableRule orderTableRule = TableRule.builder(TSSUtil.LOGICAL_TABLE_1) //邏輯表名。實體類的表名
                .actualTables(Arrays.asList(tables.trim().split(","))).dataSourceRule(dataSourceRule) //tables.trim().split(","))資料庫的表名
                .build();

        /*

        // 設定分表對映
        TableRule orderTableRule = TableRule.builder(“trade_station_year”)  
                .actualTables(Arrays.asList(“yl_2_trade_2017”,"yl_2trade_2018")).dataSourceRule(dataSourceRule) 
                .build();

          */

        // 設定分表策略,按什麼規則來分
        ShardingRule shardingRule = ShardingRule.builder().dataSourceRule(dataSourceRule)
                .tableRules(Arrays.asList(orderTableRule))
                .tableShardingStrategy(new TableShardingStrategy(

         //按這兩個欄位來分TSSUtil.COLUMN_OF_TABLE_SHARDING_1_1, TSSUtil.COLUMN_OF_TABLE_SHARDING_1_2
                        Arrays.asList(TSSUtil.COLUMN_OF_TABLE_SHARDING_1_1, TSSUtil.COLUMN_OF_TABLE_SHARDING_1_2),
                        new StationAndYearTableShardingAlgorithm(TSSUtil.COLUMN_OF_TABLE_SHARDING_1_1,
                                TSSUtil.COLUMN_OF_TABLE_SHARDING_1_2)))
                .build();

        DataSource dataSource = null;
        try {
            dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);
        } catch (SQLException e) {
            e.printStackTrace();
        }

        return dataSource;
    }

    private DataSource createDataSource() {
        // 使用druid連線資料庫
        DruidDataSource result = new DruidDataSource();
        result.setDriverClassName(driver);
        result.setUrl(url);
        result.setUsername(username);
        result.setPassword(password);
        return result;
    }
}

兩個條件的具體策略演算法

import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashSet;

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.MultipleKeysTableShardingAlgorithm;
import com.google.common.collect.Range;

public final class StationAndYearTableShardingAlgorithm implements MultipleKeysTableShardingAlgorithm {

    private String column_1 = "tradeaddress";// 欄位1名,預設為tradeaddress,即交易地點
    private String column_2 = "tradedate";// 欄位2名,預設為tradedate,即交易時間
    private String prefix = "yl_";
    private String suffix = "_trade_";

    /**
     * 表名形如YL_xxxxxx_Trade_yyyy(不區分大小寫)(xxxxxx為colum_1處理後的值,yyyy為colum_2處理後的值)
     * 
     * @param column_1
     *            欄位1名稱,預設tradeaddress
     * @param column_2
     *            欄位2名稱,預設tradedate
     */
    public StationAndYearTableShardingAlgorithm(String column_1, String column_2) {
        super();
        if (null != column_1)
            this.column_1 = column_1.toLowerCase();
        if (null != column_2)
            this.column_2 = column_2.toLowerCase();
    }

    @Override
    public Collection<String> doSharding(Collection<String> tableNames, Collection<ShardingValue<?>> shardingValues) {
        Collection<String> result = new LinkedHashSet<>(tableNames.size());

        Collection<String> result_1 = null;
        Collection<String> result_2 = null;
        for (ShardingValue<?> shardingValue : shardingValues) {
            if(null == shardingValue){
                continue;
            }
            if (shardingValue.getColumnName().equals(column_1)) {
                result_1 = doSharding(shardingValue);
            }
            if (shardingValue.getColumnName().equals(column_2)) {
                result_2 = doSharding(shardingValue);
            }
        }
        
        if (!CommonUtil.isEmtpy(result_1) && !CommonUtil.isEmtpy(result_2)) {
            for(String str_1 : result_1){
                for(String str_2 :result_2){
                    result.add(new StringBuffer(prefix).append(str_1).append(suffix).append(str_2).toString());
                }
            }
        } else if (!CommonUtil.isEmtpy(result_2)) {
            for (String tableName : tableNames) {
                String name = tableName.toLowerCase();
                for(String str_2 :result_2){
                    if (name.endsWith(new StringBuffer(suffix).append(str_2).toString()) && name.startsWith(prefix)) {
                        result.add(name);
                    }
                }
            }
        } else if (!CommonUtil.isEmtpy(result_1)) {
            for (String tableName : tableNames) {
                String name = tableName.toLowerCase();
                for(String str_1 : result_1){
                    if (name.startsWith(prefix + str_1 + suffix)) {
                        result.add(name);
                    }
                }
            }
        }
        return result;
    }

    private Collection<String> doSharding(final ShardingValue<?> shardingValue){
        Object value = shardingValue.getValue();
        Collection<?> values = shardingValue.getValues();
        Range<?> range = shardingValue.getValueRange();
        if(!CommonUtil.isEmtpy(value)){
            return doEqualSharding(value);
        }else if(!CommonUtil.isEmtpy(values)){
            return doInSharding(values);
        }else if(!CommonUtil.isEmtpy(range)){
            return doBetweenSharding(range);
        }else{
            return Collections.emptySet();
        }
    }

    
    private Collection<String> doEqualSharding(Object value) {
        if(value instanceof String){
            return doEqualSharding4String((String)value);
        }else if(value instanceof Date){
            return doEqualSharding4Date((Date)value);
        }else{
            throw new IllegalArgumentException("分表字段型別必須為java.lang.String或java.util.Date");
        }
    }
    
    private Collection<String> doEqualSharding4String(String value) {
        Collection<String> result = new LinkedHashSet<>(1);
        result.add(value);
        return result;
    }

    private Collection<String> doEqualSharding4Date(Date value) {
        Collection<String> result = new LinkedHashSet<>(1);
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(value);
        result.add(String.valueOf(calendar.get(Calendar.YEAR)));
        return result;
    }

    @SuppressWarnings("unchecked")
    private Collection<String> doInSharding(Collection<?> values) {
        for(Object value : values){
            if(null == value){
                continue;
            }
            if(value instanceof String){
                return doInSharding4String((Collection<String>) values);
            }else if(value instanceof Date){
                return doInSharding4Date((Collection<Date>) values);
            }else{
                throw new IllegalArgumentException("分表字段型別必須為java.lang.String或java.util.Date");
            }
        }
        throw new IllegalArgumentException();// in引數全為null
    }
    
    private Collection<String> doInSharding4String(Collection<String> values) {
        Collection<String> result = new LinkedHashSet<>(values.size());
        for (String value : values) {
            result.add(value);
        }
        return result;
    }

    private Collection<String> doInSharding4Date(Collection<Date> values) {
        Calendar calendar = Calendar.getInstance();
        Collection<String> result = new LinkedHashSet<>(values.size());
        for (Date value : values) {
            calendar.setTime(value);
            String year = String.valueOf(calendar.get(Calendar.YEAR));
            result.add(year);
        }
        return result;
    }

    @SuppressWarnings("unchecked")
    private Collection<String> doBetweenSharding(Range<?> range) {
        Object value = range.lowerEndpoint();
        if(null == value){
            throw new IllegalArgumentException();// between引數不能為null
        }else{
            if(value instanceof String){
                return doBetweenSharding4String((Range<String>) range);
            }else if(value instanceof Date){
                return doBetweenSharding4Date((Range<Date>) range);
            }else{
                throw new IllegalArgumentException("分表字段型別必須為java.lang.String或java.util.Date");
            }
        }
    }
    
    private Collection<String> doBetweenSharding4String(Range<String> range) {
        Collection<String> result = new LinkedHashSet<>();
        long lowerEndpoint;
        long upperEndpoint;
        try {
            lowerEndpoint = Long.valueOf(range.lowerEndpoint());
            upperEndpoint = Long.valueOf(range.upperEndpoint());
        } catch (NumberFormatException e) {
            return result;// 值不是數字字串,無法使用between,直接返回空集合
        }
        for (long i = lowerEndpoint; i <= upperEndpoint; i++) {
            result.add(String.valueOf(i));
        }
        return result;
    }

    private Collection<String> doBetweenSharding4Date(Range<Date> range) {
        Calendar calendar = Calendar.getInstance();
        Collection<String> result = new LinkedHashSet<>();
        calendar.setTime(range.lowerEndpoint());
        int lowerEndpoint = calendar.get(Calendar.YEAR);
        calendar.setTime(range.upperEndpoint());
        int upperEndpoint = calendar.get(Calendar.YEAR);
        for (int i = lowerEndpoint; i <= upperEndpoint; i++) {
            result.add(String.valueOf(i));
        }
        return result;
    }
}

單個條件的具體分表策略

import java.util.Collection;
import java.util.LinkedHashSet;

import com.dangdang.ddframe.rdb.sharding.api.ShardingValue;
import com.dangdang.ddframe.rdb.sharding.api.strategy.table.SingleKeyTableShardingAlgorithm;
import com.google.common.collect.Range;

public final class StationTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<String> {

    private String prefix = "";
    private String suffix = "";
    
    /**
     * 表名形如YL_xxxxxx_Trade(不區分大小寫)
     * @param prefix 字首,如YL_
     * @param suffix 字尾,如_Trade
     */
    public StationTableShardingAlgorithm(String prefix, String suffix) {
        super();
        if(null != prefix)
            this.prefix = prefix.toLowerCase();
        if(null != suffix)
            this.suffix = suffix.toLowerCase();
    }

    @Override
    public String doEqualSharding(final Collection<String> tableNames, final ShardingValue<String> shardingValue) {
        String value = shardingValue.getValue();
        for (String tableName : tableNames) {
            if (tableName.toLowerCase().equals(getTableName(value))) {
                return tableName;
            }
        }
        throw new IllegalArgumentException();
    }

    @Override
    public Collection<String> doInSharding(final Collection<String> tableNames,
            final ShardingValue<String> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(tableNames.size());
        for (String value : shardingValue.getValues()) {
            for (String tableName : tableNames) {
                if (tableName.toLowerCase().equals(getTableName(value))) {
                    result.add(tableName);
                }
            }
        }
        return result;
    }

    @Override
    public Collection<String> doBetweenSharding(final Collection<String> tableNames,
            final ShardingValue<String> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(tableNames.size());
        Range<String> range = shardingValue.getValueRange();
        long lowerEndpoint;
        long upperEndpoint;
        try {
            lowerEndpoint = Long.valueOf(range.lowerEndpoint());
            upperEndpoint = Long.valueOf(range.upperEndpoint());
        } catch (NumberFormatException e) {
            return result;// 值不是數字字串,無法使用between,直接返回空集合
        }
        for (long i = lowerEndpoint; i <= upperEndpoint; i++) {
            for (String tableName : tableNames) {
                if (tableName.toLowerCase().equals(getTableName(String.valueOf(i)))) {
                    result.add(tableName);
                }
            }
        }
        return result;
    }
    
    private String getTableName(String str){
        return prefix + str + suffix;
    }
}

分表符串工具類


/**
 * 分表符串工具類 TableShardingStringUtil
 * 
 */
public class TSSUtil {

    public static final String DATA_SOURCE_PREFIX = "dataSource";
    public static final String TRANSACTION_MANAGER_PREFIX = "transactionManager";
    public static final String ENTITY_MANAGER_FACTORY_PREFIX = "entityManagerFactory";

    // ***********************************第一個資料來源*********************************************************

    public static final String LABEL_1 = "TradeStationYear";// 標籤
    public static final String TRANSACTION_MANAGER_1 = TRANSACTION_MANAGER_PREFIX + LABEL_1;
    public static final String ENTITY_MANAGER_FACTORY_1 = ENTITY_MANAGER_FACTORY_PREFIX + LABEL_1;
    public static final String DATA_SOURCE_1 = DATA_SOURCE_PREFIX + LABEL_1;
    /** Repository所在包名 */
    public static final String BASE_PACKAGES_1 = "com.gxmis.afc.moduletradedata.entitytradestationyear";
    /** 實體所在包名 */
    public static final String PACKAGES_1 = "com.gxmis.afc.moduletradedata.entitytradestationyear";
    /** 邏輯表名 */
    public static final String LOGICAL_TABLE_1 = "trade_station_year";
    /** 分表字段名1 */
    public static final String COLUMN_OF_TABLE_SHARDING_1_1 = "trade_address";
    /** 分表字段名2 */
    public static final String COLUMN_OF_TABLE_SHARDING_1_2 = "trade_date";

}

分表要自己建立表