当前位置: 首页 > news >正文

Springboot JPA ShardingSphere 根据年分表java详细代码Demo

一、项目结构

src/main/java
├── com.example
│   ├── config
│   │   └── TableInitializer.java      # 动态建表配置
│   ├── entity
│   │   └── Order.java                # JPA实体类
│   ├── repository
│   │   └── OrderRepository.java      # JPA Repository接口
│   └── DemoApplication.java          # 启动类
resources
├── application.yml                   # ShardingSphere配置

二、完整代码实现

  1. 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>
  1. 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
  1. 实体类 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
    // 省略...
}
  1. 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);
}
  1. 动态建表配置 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操作完全透明化。通过动态建表机制,避免手动维护物理表结构。

相关文章:

  • Java Stream API:现代化集合处理的艺术
  • AI比人脑更强,因为被植入思维模型【49】冰山理论思维模型
  • 鱼骨图分析法实战:5步定位系统故障
  • Linux系统学习Day2——在Linux系统中开发OpenCV
  • 【微机及接口技术】- 第九章 串行通信与串行接口(上)
  • 路由表的最终地址 root 路由跟踪,最终到哪里去
  • RK-realtime Linux
  • python(49)-串口接收与发送
  • Android audio(6)-audiopolicyservice介绍
  • C++Cherno 学习笔记day17 [66]-[70] 类型双关、联合体、虚析构函数、类型转换、条件与操作断点
  • 华为OD全流程解析+备考攻略+经验分享
  • VS Code连接服务器编写Python文件
  • 【Docker】Dockerfile 编写实践
  • MYSQL数据库语法补充
  • 区间 DP 详解
  • XMLHttpRequest vs Fetch API:一场跨越时代的“浏览器宫斗剧“
  • 什么是软件测试(目的、意义、流程)
  • STM32在裸机(无RTOS)环境下,需要手动实现队列机制来替代FreeRTOS的CAN发送接收函数
  • 第四篇:系统分析师——12-16章
  • 《线性表、顺序表与链表》教案(C语言版本)
  • 安徽省委副秘书长、省委政研室主任余三元调任省社科院院长
  • 上海团队在医学顶刊连发两文,率先提出“证据污染”循证概念
  • 科创板年内第3家!健信超导IPO获受理,拟募资8.65亿
  • 退休10年后,70岁成都高新区管委会原巡视员王晋成被查
  • “海豚音”依旧,玛丽亚·凯莉本周来沪开唱
  • 山西忻州市人大常委会副主任郭建平接受审查调查