一、项目结构
src/main/java
├── com.example
│ ├── config
│ │ └── TableInitializer.java # 动态建表配置
│ ├── entity
│ │ └── Order.java # JPA实体类
│ ├── repository
│ │ └── OrderRepository.java # JPA Repository接口
│ └── DemoApplication.java # 启动类
resources
├── application.yml # ShardingSphere配置
二、完整代码实现
- pom.xml 依赖
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ShardingSphere JDBC -->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.3.2</version>
</dependency>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- 日期时间处理 -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-java8</artifactId>
</dependency>
</dependencies>
- application.yml 配置
spring:
shardingsphere:
# 数据源配置
datasource:
names: ds
ds:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test_db?serverTimezone=UTC
username: root
password: root
# 分片规则
rules:
sharding:
tables:
t_order: # 逻辑表名
actual-data-nodes: ds.t_order_$->{2020..2030} # 实际表结构
table-strategy:
standard:
sharding-column: order_time # 分片字段
sharding-algorithm-name: order-year-interval
sharding-algorithms:
order-year-interval:
type: INTERVAL
props:
datetime-pattern: "yyyy-MM-dd HH:mm:ss"
datetime-lower: "2020-01-01 00:00:00"
datetime-upper: "2030-12-31 23:59:59"
sharding-suffix-pattern: "yyyy" # 表后缀格式
datetime-interval-amount: 1 # 分片间隔1年
# 其他配置
props:
sql-show: true # 显示SQL日志
jpa:
hibernate:
ddl-auto: none # 禁用自动建表
show-sql: true
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL8Dialect
- 实体类 Order.java
package com.example.entity;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "t_order") // 对应逻辑表名
public class Order {
@Id
@GeneratedValue(generator = "snowflake") // 使用分布式ID
private Long id;
@Column(name = "order_time", nullable = false)
private LocalDateTime orderTime; // 分片关键字段
@Column(length = 50)
private String orderNo;
private BigDecimal amount;
// Getters & Setters
// 省略...
}
- Repository接口 OrderRepository.java
package com.example.repository;
import com.example.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
public interface OrderRepository extends JpaRepository<Order, Long> {
// 根据时间范围查询(自动路由到对应年度表)
List<Order> findByOrderTimeBetween(LocalDateTime start, LocalDateTime end);
}
- 动态建表配置 TableInitializer.java
package com.example.config;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.time.Year;
import java.util.stream.IntStream;
@Component
public class TableInitializer {
@Autowired
private DataSource dataSource;
@PostConstruct
public void initTables() throws SQLException {
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement()) {
// 自动创建2020-2030年的物理表
IntStream.rangeClosed(2020, 2030).forEach(year -> {
String sql = "CREATE TABLE IF NOT EXISTS t_order_" + year + " (" +
"id BIGINT PRIMARY KEY, " +
"order_time DATETIME NOT NULL, " +
"order_no VARCHAR(50), " +
"amount DECIMAL(10,2))";
try {
stmt.executeUpdate(sql);
} catch (SQLException e) {
e.printStackTrace();
}
});
}
}
}
三、测试用例
@SpringBootTest
public class OrderTest {
@Autowired
private OrderRepository orderRepository;
@Test
void testInsert() {
Order order = new Order();
order.setOrderTime(LocalDateTime.of(2023, 5, 20, 14, 30));
order.setOrderNo("NO202305201430");
order.setAmount(new BigDecimal("999.99"));
orderRepository.save(order); // 数据会插入t_order_2023表
}
@Test
void testQuery() {
LocalDateTime start = LocalDateTime.of(2023, 1, 1, 0, 0);
LocalDateTime end = LocalDateTime.of(2023, 12, 31, 23, 59);
List<Order> orders = orderRepository.findByOrderTimeBetween(start, end);
System.out.println("Query Result: " + orders.size());
}
}
四、关键点说明
1. 分片算法选择
使用INTERVAL算法实现按年分表,需明确配置:
datetime-lower/datetime-upper
:时间范围边界sharding-suffix-pattern
:表名后缀格式(yyyy表示年份)
2. 动态表管理
- 通过TableInitializer在应用启动时自动创建未来10年的物理表
- 若需要更灵活的动态扩展,可结合数据库定时任务创建新表
3. 路由规则
- 写入:根据order_time字段值自动路由到对应年度表
- 查询:若条件包含order_time范围,ShardingSphere自动合并多表结果
4. 事务处理
- 单年度操作支持本地事务
- 跨年度操作需使用
@ShardingTransactionType(TransactionType.XA)
分布式事务
五、注意事项
1. 时间字段精度
确保实体类中order_time字段类型与数据库DATETIME类型匹配
2. 表名策略扩展
若需要支持历史数据归档,可结合Hint分片强制路由到指定表
3. 索引优化
每个年度表需单独创建索引(如order_time字段索引)
4. 配置更新
当超过预设的datetime-upper年份时,需调整配置并创建新表
以上代码可实现:订单数据按年份自动存储到t_order_2023、t_order_2024等表中,且JPA操作完全透明化。通过动态建表机制,避免手动维护物理表结构。