springboot使用sharding-jdbc分库分表

1,引入依赖


    io.shardingsphere
    sharding-core
    2.0.3

2,配置数据源

取消springboot自动配置的数据源:删除application.yml里的spring.datasource节点、@EnableJpaRepositories等默认配置。

新增sharding.jdbc.datasource配置:

sharding: 
  jdbc: 
    datasource: 
      driverClassName: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost/demo
      username: root
      password: 123456

3,新建sharding-jdbc数据源

@Configuration
public class DataSourceConfig {
	/**
	 * 生成分表的数据源
	 * @return
	 * @throws SQLException
	 */
	@Bean("shardingDataSource")
	public DataSource buildShardingDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig;
        shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
        shardingRuleConfig.getBindingTableGroups().add("wc_order");
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(
        		new StandardShardingStrategyConfiguration(
        				"created_time", 
        				TableShardingAlgorithm.class.getName(), TableShardingAlgorithm.class.getName()));
        return new ShardingDataSource(shardingRuleConfig.build(createDataSourceMap()));
	}
	private Map createDataSourceMap() {
        Map result = new HashMap<>();
        result.put("ds_0", createDataSource());
        return result;
    }
    /**
     * 设置表的node
     * @return
     */
    @Bean
    TableRuleConfiguration getOrderTableRuleConfiguration() {
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
        orderTableRuleConfig.setLogicTable("wc_order");
        orderTableRuleConfig.setActualDataNodes("ds_0.wc_order_2018, ds_0.wc_order_2019");
        return orderTableRuleConfig;
    }
	@Bean
	@Primary
	@ConfigurationProperties("sharding.jdbc.datasource")
	public DataSourceProperties getProperties() {
		return new DataSourceProperties();
	}
    private DataSource createDataSource() {
    	return getProperties().initializeDataSourceBuilder().build();
    }
}

分表需要提前建好,sharding-jdbc不支持动态生成表。这里只使用一个数据源ds_0,对订单表按年份分表wc_order_2018, wc_order_2019。分表规则TableShardingAlgorithm在后面

4,分表规则

public class TableShardingAlgorithm implements PreciseShardingAlgorithm, RangeShardingAlgorithm {
	
	private final static SimpleDateFormat format = new SimpleDateFormat("yyyy");

	@Override
	public String doSharding(Collection availableTargetNames,
			PreciseShardingValue sharingValue) {
		String year = format.format(sharingValue.getValue());
		for (String targetName : availableTargetNames) {
			if (targetName.endsWith(year)) {
				return targetName;
			}
		}
		throw new RuntimeException("not table match");
	}
	@Override
	public Collection doSharding(Collection availableTargetNames,
			RangeShardingValue sharingValue) {
		Collection targetNames = new ArrayList();
		Range valueRange = sharingValue.getValueRange();
		Integer lowerYear = null, upperYear = null;
		if (valueRange.hasLowerBound()) {
			lowerYear = Integer.parseInt(format.format(valueRange.lowerEndpoint()));
		}
		if (valueRange.hasUpperBound()) {
			upperYear = Integer.parseInt(format.format(valueRange.upperEndpoint()));
		}
		for (String targetName : availableTargetNames) {
			String[] strArr = targetName.split("_");
			Integer year = Integer.parseInt(strArr[2]);
			if (upperYear == null) {
				if (year >= lowerYear) {
					targetNames.add(targetName);
				}
			} else if (lowerYear == null) {
				if (year <= upperYear) {
					targetNames.add(targetName);
				}
			} else {
				if (year >= lowerYear && year <= upperYear) {
					targetNames.add(targetName);
				}
			}
		}
		return targetNames;
	}

}

这里继承了PreciseShardingAlgorithm(处理equals和in)和RangeShardingAlgorithm(处理between)。按照年份返回对应的表。

启动之后测试一下应该可以分表插入和查询。

sharding-jdbc的限制:只有jpa的between才能使用分表规则,其他的会进行全表查询。

不支持子语句

不支持UNION 和 UNION ALL

不支持特殊INSERT

每条INSERT语句只能插入一条数据,不支持VALUES后有多行数据的语句

不支持DISTINCT聚合

扩展:规避sharding-jdbc的限制

sharding-jdbc虽然简单好用但是存在很多限制。这里分享一个规避的方法:由于只需要对订单表分表,可以声明两个数据源,一个是分表数据源,一个是正常数据源。这样其他表就不受限制,只需要修改订单表的sql。

双数据源:

springboot默认会创建数据源,但是如果自己创建数据源,默认的就不会创建。

修改DataSourceConfig

@Configuration
public class DataSourceConfig {

	@Bean("primaryDataSource")
	@Primary
	@ConfigurationProperties("sharding.jdbc.datasource")
	public DataSource buildPrimaryDataSource() {
		return DataSourceBuilder.create().build();
	}
	
	/**
	 * 生成分表的数据源
	 * @return
	 * @throws SQLException
	 */
	@Bean("shardingDataSource")
	public DataSource buildShardingDataSource() throws SQLException {
        ShardingRuleConfiguration shardingRuleConfig;
        shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(getOrderTableRuleConfiguration());
        shardingRuleConfig.getBindingTableGroups().add("wc_order");
        shardingRuleConfig.setDefaultTableShardingStrategyConfig(
        		new StandardShardingStrategyConfiguration(
        				"created_time", 
        				TableShardingAlgorithm.class.getName(), TableShardingAlgorithm.class.getName()));
        return new ShardingDataSource(shardingRuleConfig.build(createDataSourceMap()));
	}
	private Map createDataSourceMap() {
        Map result = new HashMap<>();
        result.put("ds_0", createDataSource());
        return result;
    }
    /**
     * 设置表的node
     * @return
     */
    @Bean
    TableRuleConfiguration getOrderTableRuleConfiguration() {
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration();
        orderTableRuleConfig.setLogicTable("wc_order");
        orderTableRuleConfig.setActualDataNodes("ds_0.wc_order_2018, ds_0.wc_order_2019");
        return orderTableRuleConfig;
    }
	@Bean
	@Primary
	@ConfigurationProperties("sharding.jdbc.datasource")
	public DataSourceProperties getProperties() {
		return new DataSourceProperties();
	}
    private DataSource createDataSource() {
    	return getProperties().initializeDataSourceBuilder().build();
    }
}

新建PrimaryJpaConfig

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
		repositoryFactoryBeanClass = DefaultRepositoryFactoryBean.class,
        entityManagerFactoryRef="entityManagerFactoryPrimary",
        transactionManagerRef="transactionManagerPrimary",
        basePackages= { "demo.repository" })
public class PrimaryJpaConfig {

    @Autowired 
    @Qualifier("primaryDataSource")
    private DataSource primaryDataSource;

    @Primary
    @Bean(name = "entityManagerPrimary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
    }

    @Primary
    @Bean(name = "entityManagerFactoryPrimary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary (EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(primaryDataSource)
                .properties(getVendorProperties(primaryDataSource))
                .packages("demo.domain.entity") //设置实体类所在位置
                .persistenceUnit("primaryPersistenceUnit")
                .build();
    }

    @Autowired
    private JpaProperties jpaProperties;

    private Map getVendorProperties(DataSource dataSource) {
        return jpaProperties.getHibernateProperties(dataSource);
    }

    @Primary
    @Bean(name = "transactionManagerPrimary")
    public PlatformTransactionManager transactionManagerPrimary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
    }
}

新建ShardingJpaConfig

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
		repositoryFactoryBeanClass = DefaultRepositoryFactoryBean.class,
        entityManagerFactoryRef="entityManagerFactorySecondary",
        transactionManagerRef="transactionManagerSecondary",
        basePackages= { "demo.sharding.repository" })
// 多个配置扫描同个包时,后声明的会覆盖之前的
public class ShardingJpaConfig {

    @Autowired @Qualifier("shardingDataSource")
    private DataSource secondaryDataSource;

    @Bean(name = "entityManagerSecondary")
    public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
        return entityManagerFactorySecondary(builder).getObject().createEntityManager();
    }

    @Bean(name = "entityManagerFactorySecondary")
    public LocalContainerEntityManagerFactoryBean entityManagerFactorySecondary (EntityManagerFactoryBuilder builder) {
        return builder
                .dataSource(secondaryDataSource)
                .properties(getVendorProperties(secondaryDataSource))
                .packages("demo.domain.entity", "demo.sharding.entity") //设置实体类所在位置
                .persistenceUnit("secondaryPersistenceUnit")
                .build();
    }

    @Autowired
    private JpaProperties jpaProperties;

    private Map getVendorProperties(DataSource dataSource) {
        return jpaProperties.getHibernateProperties(dataSource);
    }

    @Bean(name = "transactionManagerSecondary")
    PlatformTransactionManager transactionManagerSecondary(EntityManagerFactoryBuilder builder) {
        return new JpaTransactionManager(entityManagerFactorySecondary(builder).getObject());
    }
}

注意:repository需要分开管理,不然有可能被覆盖。entity分开管理是因为防止其他实体类的repository使用到OrderEntity,若不分开则会直接去查询order表而不是分表,分开则会直接报表不存在。将用到OrderEntity的都放到分表管理的repository中。

另外补充一下自己对jpa的理解:通过repository对表进行管理(不是entity),如果entity没有repository则会创建默认repository。通过数据源创建时的basepackages给包里的repository创建代理,并初始化entityManager。

你可能感兴趣的:(springboot使用sharding-jdbc分库分表)