使用JPA+Hibernate+HSQL 2.4.1持久化新对象时遇权限/序列异常
你遇到的核心问题是Hibernate试图调用hibernate_sequence序列生成主键,但你的HSQLDB数据库中并没有创建这个序列,因此抛出了权限/对象找不到的错误。而查询功能正常是因为表和初始数据已经存在,查询操作不需要依赖序列。下面来拆解问题根源和具体解决方案:
问题根源
你在实体类中使用了@GeneratedValue(strategy = GenerationType.AUTO),Hibernate在与HSQLDB配合时,默认会把AUTO策略解析为SEQUENCE模式,它会尝试执行call next value for hibernate_sequence来获取下一个主键值。但你的数据库是通过自定义create.sql脚本创建的,里面并没有定义这个序列,所以触发了异常。
解决方案
方案1:改用IDENTITY自增主键策略(推荐,适配HSQLDB特性)
HSQLDB原生支持自增列,你可以直接切换主键生成策略为IDENTITY,让Hibernate利用数据库的自增机制生成主键,彻底摆脱对序列的依赖。
步骤1:修改实体类的主键注解
@Entity @Table(name = "client") public class Client { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // 替换为IDENTITY策略 @Column(name = "id") private Long id; // 其他字段、getter和setter }
步骤2:确保create.sql中的id列是自增类型
检查你的sql/create.sql脚本,确保client表的id列定义为自增:
CREATE TABLE client ( id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, first_name VARCHAR(255) NOT NULL, second_name VARCHAR(255) NOT NULL, patronymic VARCHAR(255), number VARCHAR(255) NOT NULL );
方案2:手动创建Hibernate需要的序列
如果你坚持使用SEQUENCE策略,需要在create.sql中手动创建Hibernate默认使用的hibernate_sequence。
步骤1:在create.sql中添加序列创建语句
-- 先创建Hibernate默认序列 CREATE SEQUENCE hibernate_sequence START WITH 1 INCREMENT BY 1; -- 再创建client表 CREATE TABLE client ( id BIGINT PRIMARY KEY, first_name VARCHAR(255) NOT NULL, second_name VARCHAR(255) NOT NULL, patronymic VARCHAR(255), number VARCHAR(255) NOT NULL );
可选:自定义序列名称(更规范)
如果不想使用默认的hibernate_sequence,可以在实体类中指定自定义序列:
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "client_seq") @SequenceGenerator(name = "client_seq", sequenceName = "client_sequence", allocationSize = 1) @Column(name = "id") private Long id;
同时在create.sql中创建对应的序列:
CREATE SEQUENCE client_sequence START WITH 1 INCREMENT BY 1;
额外优化:清理persistence.xml的冲突配置
你的persistence.xml中有几处配置冲突,可能导致意外行为,建议调整:
javax.persistence.schema-generation.database.action和hibernate.hbm2ddl.auto同时设置,建议统一逻辑:如果依赖自定义create.sql,把hibernate.hbm2ddl.auto设为none,避免Hibernate自动修改schema。javax.persistence.sql-load-script-source和hibernate.hbn2ddl.import_files重复,保留其中一个即可。
调整后的persistence.xml示例:
<persistence-unit name="mainPU" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <class>com.haulmont.testtask.data.entity.Client</class> <exclude-unlisted-classes>true</exclude-unlisted-classes> <properties> <property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver"/> <property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:file:db-data/test_task;shutdown=true"/> <property name="javax.persistence.jdbc.user" value="SA"/> <property name="javax.persistence.jdbc.password" value=""/> <property name="javax.persistence.schema-generation.database.action" value="create"/> <property name="javax.persistence.schema-generation.create-source" value="script"/> <property name="javax.persistence.schema-generation.create-script-source" value="sql/create.sql"/> <property name="javax.persistence.sql-load-script-source" value="sql/import.sql"/> <property name="hibernate.hbm2ddl.import_files_sql_extractor" value="org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor"/> <property name="hibernate.show_sql" value="true"/> <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect"/> <property name="hibernate.hbm2ddl.auto" value="none"/> <!-- 修改为none避免冲突 --> </properties> </persistence-unit>
内容的提问来源于stack exchange,提问作者Сергей Херувимов

