1. 概述
生产环境的应用通常采用基于序列的主键生成方案,以提升效率和可扩展性。但在开发或测试阶段,我们常切换到H2这类内存数据库。
本文将深入探讨如何在H2数据库中使用序列(sequence),包括:
- 序列的基础操作(创建/查询/删除)
- 自定义序列参数
- 在JPA实体中集成序列
- 兼容Oracle等生产数据库的序列语法
2. 序列生成ID的核心优势
数据库序列提供服务端ID生成控制,在高并发场景下性能显著优于IDENTITY策略。
主流数据库如PostgreSQL、Oracle、SQL Server均支持序列。本文聚焦H2数据库的序列实现,同时展示如何通过兼容模式模拟生产环境(如Oracle)的序列行为。
⚠️ 测试环境用H2模拟生产数据库时,需特别注意语法兼容性问题
3. H2序列实战操作
3.1 基础配置
首先在Spring Boot配置文件application-h2-seq.yml
中创建嵌入式H2内存库:
spring:
datasource:
driverClassName: org.h2.Driver
url: jdbc:h2:mem:seqdb
username: sa
password:
jpa:
hibernate:
ddl-auto: none
测试类需激活h2-seq
配置:
@ExtendWith(SpringExtension.class)
@ActiveProfiles("h2-seq")
@Transactional
public class H2SeqDemoIntegrationTest {
}
3.2 序列的完整生命周期操作
以下测试演示序列的创建、取值和删除:
private final String sqlNextValueFor = "SELECT NEXT VALUE FOR my_seq";
private final String sqlNextValueFunction = "SELECT nextval('my_seq')";
@Test
void whenCreateH2SequenceWithDefaultOptions_thenGetExpectedNextValueFromSequence() {
entityManager.createNativeQuery("CREATE SEQUENCE my_seq").executeUpdate();
Long nextValue = (Long) entityManager.createNativeQuery(sqlNextValueFunction).getSingleResult();
assertEquals(1, nextValue);
nextValue = (Long) entityManager.createNativeQuery(sqlNextValueFor).getSingleResult();
assertEquals(2, nextValue);
nextValue = (Long) entityManager.createNativeQuery(sqlNextValueFunction).getSingleResult();
assertEquals(3, nextValue);
entityManager.createNativeQuery("DROP SEQUENCE my_seq").executeUpdate();
}
核心操作语法总结:
✅ 创建序列:CREATE SEQUENCE <name>
✅ 获取下一个值:
- 标准语法:
SELECT NEXT VALUE FOR <name>
- 函数语法:
SELECT nextval('<name>')
✅ 删除序列:DROP SEQUENCE <name>
💡 默认创建的序列起始值为1,步长为1
3.3 自定义序列参数
通过START WITH
和INCREMENT BY
可定制序列的起始值和步长:
@Test
void whenCustomizeH2Sequence_thenGetExpectedNextValueFromSequence() {
entityManager.createNativeQuery("CREATE SEQUENCE my_seq START WITH 1000 INCREMENT BY 10")
.executeUpdate();
Long nextValue = (Long) entityManager.createNativeQuery(sqlNextValueFor).getSingleResult();
assertEquals(1000, nextValue);
nextValue = (Long) entityManager.createNativeQuery(sqlNextValueFunction).getSingleResult();
assertEquals(1010, nextValue);
nextValue = (Long) entityManager.createNativeQuery(sqlNextValueFor).getSingleResult();
assertEquals(1020, nextValue);
entityManager.createNativeQuery("DROP SEQUENCE my_seq").executeUpdate();
}
此例创建起始值1000、步长10的序列,每次调用值递增10。
3.4 在JPA实体中集成序列
将H2序列用于JPA实体主键生成:
@Entity
@Table(name = "book")
class Book {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "book_seq_gen")
@SequenceGenerator(name = "book_seq_gen", sequenceName = "book_seq", allocationSize = 1)
private Long id;
private String title;
public Book() {
}
public Book(String title) {
this.title = title;
}
// ... getters and setters omitted
}
关键注解说明:
@GeneratedValue(strategy = GenerationType.SEQUENCE)
:启用序列生成策略@SequenceGenerator
:配置序列生成器sequenceName
:数据库序列名allocationSize = 1
:保证ID严格连续(禁用批量预分配)
验证测试:
@Test
void whenSaveEntityUsingSequence_thenCorrect() {
entityManager.createNativeQuery("CREATE SEQUENCE book_seq").executeUpdate();
Book book1 = new Book("book1");
assertNull(book1.getId());
entityManager.persist(book1);
assertEquals(1, book1.getId());
Book book2 = new Book("book2");
entityManager.persist(book2);
assertEquals(2, book2.getId());
}
⚠️ 测试中需手动创建序列(因配置了
ddl-auto=none
)。若使用create-drop
或create
,JPA会自动创建序列
4. 兼容Oracle序列语法
测试中常用H2模拟生产数据库(如Oracle),但默认H2语法与Oracle存在差异。例如Oracle获取序列值语法:
SELECT <seq_name>.nextval FROM dual
而H2默认不支持dual
表。
解决方案:在JDBC URL中设置MODE=Oracle
启用兼容模式:
spring:
datasource:
driverClassName: org.h2.Driver
url: jdbc:h2:mem:seqdb;MODE=Oracle
username: sa
password:
兼容模式测试验证:
@ExtendWith(SpringExtension.class)
@ActiveProfiles("h2-seq-oracle")
@Transactional
public class H2SeqAsOracleDemoIntegrationTest {
@Autowired
private EntityManager entityManager;
@Test
void whenCreateH2SequenceWithDefaultOptions_thenGetExpectedNextValueFromSequence() {
entityManager.createNativeQuery("CREATE SEQUENCE my_seq").executeUpdate();
String sqlNextValueFor = "SELECT NEXT VALUE FOR my_seq";
BigDecimal nextValueH2 = (BigDecimal) entityManager
.createNativeQuery(sqlNextValueFor).getSingleResult();
assertEquals(0, BigDecimal.ONE.compareTo(nextValueH2));
String sqlNextValueOralceStyle = "SELECT my_seq.nextval FROM dual";
BigDecimal nextValueOracle = (BigDecimal) entityManager.createNativeQuery(sqlNextValueOralceStyle)
.getSingleResult();
assertEquals(0, BigDecimal.TWO.compareTo(nextValueOracle));
String sqlNextValueFunction = "SELECT nextval('my_seq')";
nextValueOracle = (BigDecimal) entityManager.createNativeQuery(sqlNextValueFunction).getSingleResult();
assertEquals(0, BigDecimal.valueOf(3).compareTo(nextValueOracle));
entityManager.createNativeQuery("DROP SEQUENCE my_seq").executeUpdate();
}
}
关键发现:
✅ Oracle语法和H2原生语法均正常工作
❌ **返回类型变为BigDecimal
而非Long
**(Oracle模式特性)
5. 总结
本文系统介绍了H2数据库中序列的使用技巧,核心要点包括:
- 序列的基础操作(创建/取值/删除)
- 自定义起始值和步长的实现
- JPA实体中集成序列的最佳实践
- 通过
MODE=Oracle
实现生产环境语法兼容
合理使用H2序列兼容模式,能显著提升测试环境与生产环境的一致性,避免部署时踩坑。
完整示例代码请参考:GitHub仓库