设置Hibernate 'update'表生成策略后仍不存在laptop_seq表,POST请求保存Laptop实体时抛出异常
设置Hibernate 'update'表生成策略后仍不存在laptop_seq表,POST请求保存Laptop实体时抛出异常
我这边有一个Quarkus项目,包含以下代码和配置,在发送POST请求时遇到了Hibernate相关的异常,具体情况如下:
1. 实体类与REST资源类
这是继承自PanacheEntity的Laptop实体类,以及对应的处理POST请求的LaptopResource资源类:
import io.quarkus.hibernate.orm.panache.PanacheEntity; import jakarta.persistence.Entity; @Entity public class Laptop extends PanacheEntity { private String name; private String brand; private int ram; private int externalStorage; }
import eagle.tailung.entity.Laptop; import jakarta.transaction.Transactional; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.net.URI; import java.util.List; @Path("/laptop/") public class LaptopResource { @POST @Transactional @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public Response addLaptop(Laptop laptop){ Laptop.persist(laptop); if(laptop.isPersistent()){ return Response.created(URI.create("/laptop/"+laptop.id)).build(); } return Response.status(Response.Status.BAD_REQUEST).build(); } }
2. 应用配置文件(application.properties)
quarkus.datasource.db-kind=mysql quarkus.datasource.username=username quarkus.datasource.password=password quarkus.datasource.jdbc.url=jdbc:mysql://localhost:3306/hibernate_orm_test quarkus.datasource.jdbc.max-size=16 quarkus-hibernate-orm.database.generation=update
3. 遇到的问题
当我向/laptop发送POST请求时,抛出了Hibernate异常,请求在Swagger UI中的情况如下:
完整异常栈信息:
"details": "Error id bedefe4e-3ca0-4731-b2fa-d183959db81d-5, org.jboss.resteasy.spi.UnhandledException: org.hibernate.HibernateException: Could not apply work", "stack": "org.jboss.resteasy.spi.UnhandledException: org.hibernate.HibernateException: Could not apply work at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:107) at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:344) at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:205) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:452) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invokePropagateNotFound$6(SynchronousDispatcher.java:275) at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321) at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:157) at org.jboss.resteasy.core.SynchronousDispatcher.invokePropagateNotFound(SynchronousDispatcher.java:260) at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:86) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151) at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:97) at io.quarkus.vertx.core.runtime.VertxCoreRecorder$15.runWith(VertxCoreRecorder.java:639) at org.jboss.threads.EnhancedQueueExecutor$Task.doRunWith(EnhancedQueueExecutor.java:2675) at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2654) at org.jboss.threads.EnhancedQueueExecutor.runThreadBody(EnhancedQueueExecutor.java:1627) at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1594) at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:11) at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:11) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: org.hibernate.HibernateException: Could not apply work at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.doInNewTransaction(JtaIsolationDelegate.java:173) at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.lambda$delegateWork$3(JtaIsolationDelegate.java:90) at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.doInSuspendedTransaction(JtaIsolationDelegate.java:125) at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.delegateWork(JtaIsolationDelegate.java:88) at org.hibernate.id.enhanced.TableStructure$1.getNextValue(TableStructure.java:135) at org.hibernate.id.enhanced.PooledLoOptimizer.generate(PooledLoOptimizer.java:65) at org.hibernate.id.enhanced.SequenceStyleGenerator.generate(SequenceStyleGenerator.java:585) at org.hibernate.id.IdentifierGenerator.generate(IdentifierGenerator.java:135) at org.hibernate.event.internal.AbstractSaveEventListener.generateId(AbstractSaveEventListener.java:155) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:126) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:177) at org.hibernate.event.internal.DefaultPersistEventListener.persist(DefaultPersistEventListener.java:95) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:79) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:761) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:745) at io.quarkus.hibernate.orm.runtime.session.TransactionScopedSession.persist(TransactionScopedSession.java:146) at org.hibernate.engine.spi.SessionLazyDelegator.persist(SessionLazyDelegator.java:282) at org.hibernate.Session_OpdLahisOZ9nWRPXMsEFQmQU03A_Synthetic_ClientProxy.persist(Unknown Source) at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.persist(AbstractJpaOperations.java:105) at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.persist(AbstractJpaOperations.java:100) at io.quarkus.hibernate.orm.panache.common.runtime.AbstractJpaOperations.persist(AbstractJpaOperations.java:116) at io.quarkus.hibernate.orm.panache.PanacheEntityBase.persist(PanacheEntityBase.java:768) at eagle.tailung.entity.Laptop.persist(Laptop.java) at eagle.tailung.Resource.LaptopResource.addLaptop(LaptopResource.java:24) at eagle.tailung.Resource.LaptopResource_Subclass.addLaptop$$superforward(Unknown Source) at eagle.tailung.Resource.LaptopResource_Subclass$$function$$3.apply(Unknown Source) at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:73) at io.quarkus.arc.impl.AroundInvokeInvocationContext.proceed(AroundInvokeInvocationContext.java:62) at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:136) at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.invokeInOurTx(TransactionalInterceptorBase.java:107) at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.doIntercept(TransactionalInterceptorRequired.java:38) at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorBase.intercept(TransactionalInterceptorBase.java:61) at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired.intercept(TransactionalInterceptorRequired.java:32) at io.quarkus.narayana.jta.runtime.interceptor.TransactionalInterceptorRequired_Bean.intercept(Unknown Source) at io.quarkus.arc.impl.InterceptorInvocation.invoke(InterceptorInvocation.java:42) at io.quarkus.arc.impl.AroundInvokeInvocationContext.perform(AroundInvokeInvocationContext.java:30) at io.quarkus.arc.impl.InvocationContexts.performAroundInvoke(InvocationContexts.java:27) at eagle.tailung.Resource.LaptopResource_Subclass.addLaptop(Unknown Source) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:154) at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:118) at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:560) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:452) at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:413) at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321) at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:415) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:378) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:356) at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:70) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:429) ... 17 more Caused by: org.hibernate.HibernateException: Unable to perform isolated work at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.doTheWork(JtaIsolationDelegate.java:193) at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.lambda$delegateWork$2(JtaIsolationDelegate.java:90) at org.hibernate.resource.transaction.backend.jta.internal.JtaIsolationDelegate.doInSuspendedTransaction(JtaIsolationDelegate.java:125) ...
问题分析与解决方案
从异常栈能看出,核心问题是:PanacheEntity默认采用基于序列的ID生成策略,但MySQL不支持原生序列,Hibernate会通过创建laptop_seq表来模拟序列;而你配置的quarkus-hibernate-orm.database.generation=update只会更新已存在的表结构,不会主动创建这个模拟序列的新表,导致保存实体时找不到序列表抛出异常。
这里提供两种直接的解决办法:
方法1:适配MySQL自增主键,修改实体类的ID生成策略
放弃继承PanacheEntity,改为继承PanacheEntityBase,手动定义适配MySQL的自增ID:
import io.quarkus.hibernate.orm.panache.PanacheEntityBase; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class Laptop extends PanacheEntityBase { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) public Long id; private String name; private String brand; private int ram; private int externalStorage; }
这种方式完全适配MySQL的自增主键特性,不需要额外的序列表,是最贴合MySQL生态的方案。
方法2:初始化序列表后改回原配置
- 临时启动初始化:把
quarkus-hibernate-orm.database.generation临时改为create,启动一次项目,Hibernate会自动创建所有需要的表(包括laptop_seq),之后再改回update即可。 - 手动创建序列表:直接在MySQL中执行SQL创建
laptop_seq表:CREATE TABLE laptop_seq ( next_val BIGINT ) ENGINE=InnoDB; INSERT INTO laptop_seq VALUES (1);
备注:内容来源于stack exchange,提问作者mildgrey
相关产品推荐
相关产品推荐

