jakarta-ee - 仅当在 EJB 调用中捕获到 RuntimeException 时才阻止将 JTA 事务标记为回滚
问题描述
我在 Wildfly 上部署了一个参与 JTA 事务的有状态 EJB 3。此 EJB 是 Keycloak 的扩展用户提供程序。
其中一个 EJB 操作在某个时间点被调用,并可能产生 RuntimeException。我需要提交全局事务,所以我在 ejb 方法中添加了一个 try-catch 块。
@Stateful
@Local(EjbUserStorageProvider.class)
public class EjbUserStorageProvider implements UserStorageProvider,
UserLookupProvider,
UserRegistrationProvider,
UserQueryProvider,
CredentialInputUpdater,
CredentialInputValidator,
OnUserCache
{
...
@Override
public int getUsersCount(RealmModel realm) {
try {
Object count = em.createNamedQuery("getUserCount").getSingleResult();
return ((Number)count).intValue();
} catch(RuntimeException e) {
e.printStackTrace();
return 0;
}
}
}
问题是,尽管捕获了异常,事务还是回滚了。
这是有问题的位:
org.keycloak.models.utils.KeycloakModelUtils
public static void runJobInTransaction(KeycloakSessionFactory factory, KeycloakSessionTask task) {
KeycloakSession session = factory.create();
KeycloakTransaction tx = session.getTransactionManager();
try {
tx.begin();
task.run(session);
if (tx.isActive()) {
if (tx.getRollbackOnly()) {
tx.rollback();
} else {
tx.commit();
}
}
} catch (RuntimeException re) {
if (tx.isActive()) {
tx.rollback();
}
throw re;
} finally {
session.close();
}
}
由于某种原因tx.getRollbackOnly()
返回 true,因此 JTA 事务被回滚。
如何防止事务被标记为仅回滚?
更新
我尝试使用 @Transactional 但被忽略了:
@Transactional(value = Transactional.TxType.NEVER, dontRollbackOn = {RuntimeException.class, PersistenceException.class})
@Override
public int getUsersCount(RealmModel realm) {
我也使用过@TransactionAttribute(NOT_SUPPORTED)
,但在这种情况下 Keycloak 无法启动:
09:36:30,784 ERROR [org.jboss.as.ejb3.invocation] (ServerService Thread Pool -- 51) WFLYEJB0034: EJB Invocation failed on component EjbUserStorageProvider for method public int es.mma.edm.keycloak.storage.user.EjbUserStorageProvider.getUsersCount(org.keycloak.models.RealmModel): javax.ejb.ConcurrentAccessTimeoutException: WFLYEJB0228: EJB 3.1 FR 4.3.14.1 concurrent access timeout on EjbUserStorageProvider - could not obtain lock within 5000 MILLISECONDS
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.component.stateful.StatefulSessionSynchronizationInterceptor.processInvocation(StatefulSessionSynchronizationInterceptor.java:94)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ee@7.2.0.GA-redhat-00005//org.jboss.as.ee.concurrent.ConcurrentContextInterceptor.processInvocation(ConcurrentContextInterceptor.java:45)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InitialInterceptor.processInvocation(InitialInterceptor.java:40)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:53)
at org.jboss.as.ee@7.2.0.GA-redhat-00005//org.jboss.as.ee.component.interceptors.ComponentDispatcherInterceptor.processInvocation(ComponentDispatcherInterceptor.java:52)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.component.stateful.StatefulComponentInstanceInterceptor.processInvocation(StatefulComponentInstanceInterceptor.java:59)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.component.interceptors.AdditionalSetupInterceptor.processInvocation(AdditionalSetupInterceptor.java:54)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInNoTx(CMTTxInterceptor.java:216)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.tx.CMTTxInterceptor.notSupported(CMTTxInterceptor.java:337)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.tx.CMTTxInterceptor.processInvocation(CMTTxInterceptor.java:142)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor.processInvocation(CurrentInvocationContextInterceptor.java:41)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.component.invocationmetrics.WaitTimeInterceptor.processInvocation(WaitTimeInterceptor.java:47)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.security.SecurityContextInterceptor.processInvocation(SecurityContextInterceptor.java:100)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.deployment.processors.StartupAwaitInterceptor.processInvocation(StartupAwaitInterceptor.java:22)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.component.interceptors.ShutDownInterceptorFactory$1.processInvocation(ShutDownInterceptorFactory.java:64)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ejb3@7.2.0.GA-redhat-00005//org.jboss.as.ejb3.component.interceptors.LoggingInterceptor.processInvocation(LoggingInterceptor.java:67)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.as.ee@7.2.0.GA-redhat-00005//org.jboss.as.ee.component.NamespaceContextInterceptor.processInvocation(NamespaceContextInterceptor.java:50)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.ContextClassLoaderInterceptor.processInvocation(ContextClassLoaderInterceptor.java:60)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.run(InterceptorContext.java:438)
at org.wildfly.security.elytron-private@1.6.1.Final-redhat-00001//org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:619)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.AccessCheckingInterceptor.processInvocation(AccessCheckingInterceptor.java:57)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:422)
at org.jboss.invocation@1.5.1.Final-redhat-1//org.jboss.invocation.ChainedInterceptor.processInvocation(ChainedInterceptor.java:53)
at org.jboss.as.ee@7.2.0.GA-redhat-00005//org.jboss.as.ee.component.ViewService$View.invoke(ViewService.java:198)
at org.jboss.as.ee@7.2.0.GA-redhat-00005//org.jboss.as.ee.component.ViewDescription$1.processInvocation(ViewDescription.java:185)
at org.jboss.as.ee@7.2.0.GA-redhat-00005//org.jboss.as.ee.component.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:81)
at deployment.edm-keycloak-user-storage.jar//es.mma.edm.keycloak.storage.user.EjbUserStorageProvider$$$view1.getUsersCount(Unknown Source)
at org.keycloak.keycloak-services@4.8.3.Final-redhat-00001//org.keycloak.storage.UserStorageManager.getUsersCount(UserStorageManager.java:453)
at org.keycloak.keycloak-model-infinispan@4.8.3.Final-redhat-00001//org.keycloak.models.cache.infinispan.UserCacheSession.getUsersCount(UserCacheSession.java:543)
at org.keycloak.keycloak-model-infinispan@4.8.3.Final-redhat-00001//org.keycloak.models.cache.infinispan.UserCacheSession.getUsersCount(UserCacheSession.java:548)
at org.keycloak.keycloak-services@4.8.3.Final-redhat-00001//org.keycloak.services.managers.ApplianceBootstrap.isNoMasterUser(ApplianceBootstrap.java:55)
at org.keycloak.keycloak-services@4.8.3.Final-redhat-00001//org.keycloak.services.resources.KeycloakApplication$2.run(KeycloakApplication.java:168)
at org.keycloak.keycloak-server-spi-private@4.8.3.Final-redhat-00001//org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction(KeycloakModelUtils.java:227)
at org.keycloak.keycloak-services@4.8.3.Final-redhat-00001//org.keycloak.services.resources.KeycloakApplication.<init>(KeycloakApplication.java:164)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at org.jboss.resteasy.resteasy-jaxrs@3.6.1.SP2-redhat-00001//org.jboss.resteasy.core.ConstructorInjectorImpl.construct(ConstructorInjectorImpl.java:154)
at org.jboss.resteasy.resteasy-jaxrs@3.6.1.SP2-redhat-00001//org.jboss.resteasy.spi.ResteasyProviderFactory.createProviderInstance(ResteasyProviderFactory.java:2757)
at org.jboss.resteasy.resteasy-jaxrs@3.6.1.SP2-redhat-00001//org.jboss.resteasy.spi.ResteasyDeployment.createApplication(ResteasyDeployment.java:363)
at org.jboss.resteasy.resteasy-jaxrs@3.6.1.SP2-redhat-00001//org.jboss.resteasy.spi.ResteasyDeployment.startInternal(ResteasyDeployment.java:276)
at org.jboss.resteasy.resteasy-jaxrs@3.6.1.SP2-redhat-00001//org.jboss.resteasy.spi.ResteasyDeployment.start(ResteasyDeployment.java:88)
at org.jboss.resteasy.resteasy-jaxrs@3.6.1.SP2-redhat-00001//org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.init(ServletContainerDispatcher.java:119)
at org.jboss.resteasy.resteasy-jaxrs@3.6.1.SP2-redhat-00001//org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.init(HttpServletDispatcher.java:36)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.LifecyleInterceptorInvocation.proceed(LifecyleInterceptorInvocation.java:117)
at org.wildfly.extension.undertow@7.2.0.GA-redhat-00005//org.wildfly.extension.undertow.security.RunAsLifecycleInterceptor.init(RunAsLifecycleInterceptor.java:78)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.LifecyleInterceptorInvocation.proceed(LifecyleInterceptorInvocation.java:103)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.ManagedServlet$DefaultInstanceStrategy.start(ManagedServlet.java:303)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.ManagedServlet.createServlet(ManagedServlet.java:143)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.DeploymentManagerImpl$2.call(DeploymentManagerImpl.java:583)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.DeploymentManagerImpl$2.call(DeploymentManagerImpl.java:554)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:42)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
at org.wildfly.extension.undertow@7.2.0.GA-redhat-00005//org.wildfly.extension.undertow.security.SecurityContextThreadSetupAction.lambda$create$0(SecurityContextThreadSetupAction.java:105)
at org.wildfly.extension.undertow@7.2.0.GA-redhat-00005//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow@7.2.0.GA-redhat-00005//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow@7.2.0.GA-redhat-00005//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at org.wildfly.extension.undertow@7.2.0.GA-redhat-00005//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService$UndertowThreadSetupAction.lambda$create$0(UndertowDeploymentInfoService.java:1502)
at io.undertow.servlet@2.0.15.Final-redhat-00001//io.undertow.servlet.core.DeploymentManagerImpl.start(DeploymentManagerImpl.java:596)
at org.wildfly.extension.undertow@7.2.0.GA-redhat-00005//org.wildfly.extension.undertow.deployment.UndertowDeploymentService.startContext(UndertowDeploymentService.java:97)
at org.wildfly.extension.undertow@7.2.0.GA-redhat-00005//org.wildfly.extension.undertow.deployment.UndertowDeploymentService$1.run(UndertowDeploymentService.java:78)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.jboss.threads@2.3.2.Final-redhat-1//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads@2.3.2.Final-redhat-1//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1985)
at org.jboss.threads@2.3.2.Final-redhat-1//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1487)
at org.jboss.threads@2.3.2.Final-redhat-1//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1378)
at java.base/java.lang.Thread.run(Thread.java:834)
at org.jboss.threads@2.3.2.Final-redhat-1//org.jboss.threads.JBossThread.run(JBossThread.java:485)
解决方案
这是 JPA 规范要求的预期行为(第 79 页第 3.1.1 节)
如果持久性上下文连接到该事务,则由 EntityManager 接口的方法(而非 LockTimeoutException)引发的运行时异常将导致当前事务被标记为回滚。
要解决此问题,您需要声明要在新事务中执行的方法,使用REQUIRES_NEW
asTransactionAttribute
推荐阅读
- jquery - 如何在 jqGrid 中进行分页?
- ios - 使用未解析的标识符“UIScreen”;你的意思是“NSScreen”吗?DynamicTypes 在 iOS 上运行良好,但在 macOS 上却不行
- r - YAML 标头中的乳胶外壳转义选项不使用
- c - 使用 getline 时出现分段错误(核心转储)
- bash - 我在这个 bash 脚本中做错了什么?
- android - 使用协程的 Firebase 实时快照监听器
- c# - 如何使用 polly 启用超时策略
- python - python函数在tkinter中迭代
- java - 如何根据文件中的数据类型从 CSV 文件中读取 2 个 ArrayList?
- swift - FlatMapLatest 跳过触发器,直到最新的 observable 完成