【问题标题】:Passing enum as method parameter causes Hibernate exception将枚举作为方法参数传递会导致 Hibernate 异常
【发布时间】:2021-06-30 16:49:06
【问题描述】:

我正在编写一个使用 Hibernate 访问数据库的 Micronaut 应用程序。当我尝试使用 enum 值查询实体时,就会遇到我遇到的问题。如果我将枚举的实例传递到 repo 中,那么一切正常。但是,如果我将枚举的一个实例传递给服务层(标记为@Transactional),那么我会得到一个 Hibernate 异常。我不知道为什么,因为我不相信枚举是 Hibernate 托管 bean。

我将发布一个简化的示例。

控制器方法:

@Get("/api/frames")
@TransactionalAdvice(readOnly = true)
@Transactional
fun findFrames(): HttpResponse<Frame> {
    val response = frameService.findFrames(FrameState.SUCCESS)
    return HttpResponse.ok(response)
}

请注意,如果上面的方法没有标记为@Transactional,那么这个端点会导致Hibernate异常,我不知道为什么会这样。

服务方式:

@TransactionalAdvice(readOnly = true)
@Transactional
open fun findFrames(
    state: FrameState
): List<Frame> {
    return frameRepository.findFrames(state)
}

请注意,这也被标记为事务性的。

Repository Method(类用@Repository注解):

fun findFrames(
    state: FrameState?
): List<Frame> {
    val criteriaBuilder = entityManager.criteriaBuilder
    val criteriaQuery = criteriaBuilder.createQuery(Frame::class.java)
    val root = criteriaQuery.from(Frame::class.java)

    val predicates = mutableListOf<Predicate>()
    state?.let { predicates.add(criteriaBuilder.equal(root.get<FrameState>("state"), state)) }

    criteriaQuery.select(root).where(*predicates.toTypedArray())

    val query = entityManager.createQuery(criteriaQuery)
    return query.resultList
}

最后是实体:

@Entity
@Table(name = "frame")
data class Frame(

    @Column(nullable = false)
    var title: String,

    @Column(nullable = false)
    var width: Short,

    @Column(nullable = false)
    var height: Short,

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    var state: FrameState,

) {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "frame_id", insertable = false, nullable = false, updatable = false)
    var id: Int = 0
}

这是当控制器方法未标记为@Transactional时引发的异常

org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
at io.micronaut.transaction.hibernate5.MicronautSessionContext.currentSession(MicronautSessionContext.java:100)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:479)
at io.micronaut.configuration.hibernate.jpa.TransactionalSessionInterceptor.intercept(TransactionalSessionInterceptor.java:56)
at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:96)
at io.micronaut.configuration.hibernate.jpa.TransactionalSession$Intercepted.getCriteriaBuilder(Unknown Source)
at com.example.repository.FrameRepository.findFrames(FrameRepository.kt:27)
at com.example.service.FrameService.findFrames(FrameService.kt:73)
at com.example.controller.FrameController.findFrames(FrameController.kt:168)
at com.example.controller.$FramesControllerDefinition$Intercepted.$$access$$findFrames(Unknown Source)
at com.example.controller.$FrameControllerDefinition$$exec3.invokeInternal(Unknown Source)
at io.micronaut.context.AbstractExecutableMethod.invoke(AbstractExecutableMethod.java:151)
at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:87)
at io.micronaut.validation.ValidatingInterceptor.validateReturnExecutableValidator(ValidatingInterceptor.java:152)
at io.micronaut.validation.ValidatingInterceptor.intercept(ValidatingInterceptor.java:100)
at io.micronaut.aop.chain.MethodInterceptorChain.proceed(MethodInterceptorChain.java:96)
at com.example.controller.$FrameControllerDefinition$Intercepted.findFrames(Unknown Source)
at com.example.controller.$FrameControllerDefinition$$exec3.invokeInternal(Unknown Source)
at ...

我的问题是,如果 FrameState 只是一个枚举而不是 Hibernate 托管 bean,为什么我需要将我的控制器方法标记为 @Transactional

注意,执行此行时会抛出异常:

val criteriaBuilder = entityManager.criteriaBuilder

【问题讨论】:

  • 你为什么不将 FrameRepository 定义为一个接口,让 Micronaut Data 通过定义一个名为 List&lt;Frame&gt; findAllByState(FrameState state) 的 finder 方法为你做查询?
  • @saw303 实际上,查询要复杂得多。我已经对其进行了简化,但同时尝试使其尽可能相似以更好地描述问题
  • 您可以发布堆栈跟踪的causedBy 部分吗?
  • @PhilBa 这是我拥有的所有信息。原因为空

标签: hibernate jpa micronaut micronaut-data


【解决方案1】:

我猜它没有连接到 Enum,这是因为 Hibernate 管理会话和连接的方式。

简单来说:通过将函数注释为@Transactional 会打开一个新会话,您可能需要一个会话来执行要在存储库函数中执行的操作。

我敢打赌,只需将 Repository.findFrames 函数注释为 @Transactional 并将其余部分保持原样就足够了。

【讨论】:

  • 是的,但我的服务已经标记为@Transactional,我必须将我的控制器标记为@Transactional 才能使其工作
猜你喜欢
  • 2012-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多