以下是对 MyBatis-Plus @KeySequence 注解的全面解析,特别针对 Oracle 序列支持。


一、核心概念

概念 说明
作用 为 Oracle 数据库提供序列支持,实现主键值自动生成
核心属性 value(序列名称), clazz(主键数据类型)
适用数据库 Oracle (PostgreSQL 等支持序列的数据库也可使用)
配合注解 需与 @TableId(type = IdType.INPUT) 一起使用

二、详细操作步骤

1. 创建 Oracle 序列

-- 创建序列
CREATE SEQUENCE SEQ_USER_ID 
START WITH 1000 
INCREMENT BY 1 
NOCACHE 
NOCYCLE;

-- 验证序列
SELECT SEQ_USER_ID.NEXTVAL FROM DUAL;

2. 添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.0+</version>
</dependency>
<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>19.3.0.0</version>
</dependency>

3. 实体类配置

@Data
@TableName("T_USER")
@KeySequence(value = "SEQ_USER_ID", clazz = Long.class) // 关键注解
public class User {
    
    // 必须使用 INPUT 类型
    @TableId(value = "USER_ID", type = IdType.INPUT)
    private Long userId;
    
    private String userName;
}

4. 配置序列生成器 (MyBatis-Plus 3.3.0+ 可跳过)

@Configuration
public class MybatisPlusConfig {

    // 仅需在 3.3.0 以下版本配置
    @Bean
    public OracleKeyGenerator oracleKeyGenerator(){
        return new OracleKeyGenerator();
    }
}

5. 插入数据

User user = new User();
user.setUserName("OracleUser");
userMapper.insert(user); // 自动获取序列值填充主键

System.out.println("生成的主键ID: " + user.getUserId());
// 输出: 生成的主键ID: 1000

三、常见错误与解决方案

  1. 序列不存在错误
    报错: ORA-02289: sequence does not exist
    解决:

    • 检查 @KeySequence 注解的序列名是否与数据库一致
    • 确认数据库用户是否有序列访问权限
  2. 主键未填充
    现象: 插入后 userId 仍为 null
    解决:

    • 确认使用 @TableId(type = IdType.INPUT)
    • 检查是否配置了 OracleKeyGenerator(3.3.0 以下版本)
  3. 类型转换异常
    报错: ClassCastException
    解决:

    • 确保 @KeySequence(clazz = Long.class) 与主键字段类型匹配
    • Oracle 序列返回的是 BigDecimal,需正确转换

四、关键注意事项

  1. 版本差异处理

    • MyBatis-Plus ≥ 3.3.0:无需额外配置 OracleKeyGenerator
    • MyBatis-Plus < 3.3.0:必须配置 OracleKeyGenerator Bean
  2. 事务内序列使用

    @Transactional
    public void createUser(User user) {
        userMapper.insert(user); // 此处获取序列值
        // 后续操作可直接使用 user.getUserId()
    }
    
  3. 批量插入处理

    List<User> users = ...;
    users.forEach(user -> {
        // 必须逐条设置序列值
        userMapper.insert(user); 
    });
    

五、性能优化建议

  1. 序列缓存优化

    CREATE SEQUENCE SEQ_USER_ID
        START WITH 1000
        INCREMENT BY 1
        CACHE 20;  -- 增加缓存减少I/O
    
  2. 批量获取序列值 (Oracle 12c+)

    // 自定义获取多个序列值
    List<Long> getNextIds(String seqName, int count) {
        String sql = "SELECT " + seqName + 
                     ".NEXTVAL FROM DUAL CONNECT BY LEVEL <= " + count;
        return jdbcTemplate.queryForList(sql, Long.class);
    }
    
  3. 连接池配置

    spring:
      datasource:
        hikari:
          maximum-pool-size: 20
          connection-test-query: SELECT 1 FROM DUAL
    

六、最佳实践

  1. 统一序列命名规范

    // 使用常量管理序列名
    public interface Sequences {
        String USER_ID = "SEQ_USER_ID";
        String ORDER_ID = "SEQ_ORDER_ID";
    }
    
    @KeySequence(value = Sequences.USER_ID, clazz = Long.class)
    
  2. 多数据源适配

    @KeySequence(value = "SEQ_USER_ID", 
                 clazz = Long.class,
                 dbType = DbType.ORACLE)
    public class User {...}
    
  3. 历史数据兼容

    @TableId(type = IdType.INPUT)
    private String userId; // 使用字符型主键兼容旧系统
    
    // 自定义ID生成器
    public class CustomIdGenerator implements IdentifierGenerator {
        @Override
        public Number nextId(Object entity) {
            return "ID_" + Sequence.nextVal();
        }
    }
    

七、完整示例代码

// 实体类
@Data
@TableName("T_ORDER")
@KeySequence(value = "SEQ_ORDER_ID", clazz = Long.class)
public class Order {
    @TableId(value = "ORDER_ID", type = IdType.INPUT)
    private Long orderId;
    
    private String orderNo;
    private BigDecimal amount;
}

// 服务层
@Service
public class OrderService {
    @Autowired
    private OrderMapper orderMapper;
    
    @Transactional
    public Long createOrder(OrderDTO dto) {
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setAmount(dto.getAmount());
        
        orderMapper.insert(order); // 自动填充orderId
        
        // 记录日志
        logService.log("创建订单, ID:" + order.getOrderId());
        return order.getOrderId();
    }
    
    private String generateOrderNo() {
        return "ORD" + System.currentTimeMillis();
    }
}

八、故障排查指南

  1. 启用 SQL 日志

    mybatis-plus:
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
  2. 检查执行的 SQL

    -- 期望执行的SQL
    INSERT INTO T_USER(USER_ID, USER_NAME) 
    VALUES(SEQ_USER_ID.NEXTVAL, ?)
    
  3. 验证序列权限

    SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME='SEQ_USER_ID';
    GRANT SELECT ON SEQ_USER_ID TO APP_USER;
    

通过以上详细说明,你可以快速掌握 @KeySequence 在 Oracle 环境下的应用,特别需要注意版本差异批量插入的特殊处理。建议在开发环境中使用 Docker 快速搭建 Oracle 测试环境验证序列行为。