【发布时间】: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<Frame> findAllByState(FrameState state)的 finder 方法为你做查询? -
@saw303 实际上,查询要复杂得多。我已经对其进行了简化,但同时尝试使其尽可能相似以更好地描述问题
-
您可以发布堆栈跟踪的
causedBy部分吗? -
@PhilBa 这是我拥有的所有信息。原因为空
标签: hibernate jpa micronaut micronaut-data