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