以下是对 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
三、常见错误与解决方案
序列不存在错误
报错:ORA-02289: sequence does not exist
解决:- 检查
@KeySequence
注解的序列名是否与数据库一致 - 确认数据库用户是否有序列访问权限
- 检查
主键未填充
现象: 插入后userId
仍为null
解决:- 确认使用
@TableId(type = IdType.INPUT)
- 检查是否配置了
OracleKeyGenerator
(3.3.0 以下版本)
- 确认使用
类型转换异常
报错:ClassCastException
解决:- 确保
@KeySequence(clazz = Long.class)
与主键字段类型匹配 - Oracle 序列返回的是
BigDecimal
,需正确转换
- 确保
四、关键注意事项
版本差异处理
- MyBatis-Plus ≥ 3.3.0:无需额外配置
OracleKeyGenerator
- MyBatis-Plus < 3.3.0:必须配置
OracleKeyGenerator
Bean
- MyBatis-Plus ≥ 3.3.0:无需额外配置
事务内序列使用
@Transactional public void createUser(User user) { userMapper.insert(user); // 此处获取序列值 // 后续操作可直接使用 user.getUserId() }
批量插入处理
List<User> users = ...; users.forEach(user -> { // 必须逐条设置序列值 userMapper.insert(user); });
五、性能优化建议
序列缓存优化
CREATE SEQUENCE SEQ_USER_ID START WITH 1000 INCREMENT BY 1 CACHE 20; -- 增加缓存减少I/O
批量获取序列值 (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); }
连接池配置
spring: datasource: hikari: maximum-pool-size: 20 connection-test-query: SELECT 1 FROM DUAL
六、最佳实践
统一序列命名规范
// 使用常量管理序列名 public interface Sequences { String USER_ID = "SEQ_USER_ID"; String ORDER_ID = "SEQ_ORDER_ID"; } @KeySequence(value = Sequences.USER_ID, clazz = Long.class)
多数据源适配
@KeySequence(value = "SEQ_USER_ID", clazz = Long.class, dbType = DbType.ORACLE) public class User {...}
历史数据兼容
@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();
}
}
八、故障排查指南
启用 SQL 日志
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
检查执行的 SQL
-- 期望执行的SQL INSERT INTO T_USER(USER_ID, USER_NAME) VALUES(SEQ_USER_ID.NEXTVAL, ?)
验证序列权限
SELECT * FROM USER_SEQUENCES WHERE SEQUENCE_NAME='SEQ_USER_ID'; GRANT SELECT ON SEQ_USER_ID TO APP_USER;
通过以上详细说明,你可以快速掌握 @KeySequence
在 Oracle 环境下的应用,特别需要注意版本差异和批量插入的特殊处理。建议在开发环境中使用 Docker 快速搭建 Oracle 测试环境验证序列行为。