【问题标题】:Concurrency not handled in Eclipse Link when using Spring JpaTransactionManager使用 Spring JpaTransactionManager 时,Eclipse Link 中未处理并发
【发布时间】:2017-05-24 09:11:22
【问题描述】:

我们正在使用多租户事务管理器,如上文所述,注册日期:2014 年 11 月。我们正在使用 Spring Data,并且我们正在根据用户 ID 和密码识别租户,即我们首先不知道租户。该应用程序运行良好。我们正在使用带有基本身份验证的弹簧安全性。并且应用程序通过 Rest API 公开。

当并发的不同租户同时使用事务管理器时会出现问题。我们使用 TenantResolver 类来查找租户

我们还有另外 2 个消息流,每分钟大约有 40 条消息和 10 条消息,它们使用连接管理器来查找租户并基于此执行事务。

这里的日志:

MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> ACTUAL_TENANT : vce 07:51:05,720 信息 [xxx.integration.jms.VehicleRepositoryListener] (vrMessageListenerContainer-1) [] VehicleRepositoryListener.onMessage()--> 处理成功 在 VehicleRepositoryListener 07:51:05,768 错误中完成 [xxx.integration.services.utils.TenantUtils] (vrMessageListenerContainer-1) [] TenantUtils.setTenantByProductClass -> 设置租户 org.springframework.orm.jpa.JpaSystemException 时出错:异常 [EclipseLink-28013](Eclipse 持久性服务 - 2.6.0.v20150309-bf26070):org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:无法部署 PersistenceUnit [default] in 无效状态 [DeployFailed]。内部异常: javax.persistence.PersistenceException:异常 [EclipseLink-28019] (Eclipse 持久性服务 - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:PersistenceUnit [默认] 的部署失败。 关闭此 PersistenceUnit 的所有工厂。内部异常: javax.persistence.PersistenceException:异常 [EclipseLink-28009] (Eclipse 持久性服务 - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:试图重新部署一个名为 /file:/opt/jboss64/modules/system/layers/base/org/jboss/as/ejb3/main/timers/_default 不关闭它。嵌套异常是 javax.persistence.PersistenceException:异常 [EclipseLink-28013] (Eclipse 持久性服务 - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:无法部署 PersistenceUnit [default] in 无效状态 [DeployFailed]。内部异常: javax.persistence.PersistenceException:异常 [EclipseLink-28019] (Eclipse 持久性服务 - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:PersistenceUnit [默认] 的部署失败。 关闭此 PersistenceUnit 的所有工厂。内部异常: javax.persistence.PersistenceException:异常 [EclipseLink-28009] (Eclipse 持久性服务 - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:试图重新部署一个名为 /file:/opt/jboss64/modules/system/layers/base/org/jboss/as/ejb3/main/timers/_default 没有关闭它。 07:51:05,774 信息 [xxxintegration.services.XXXUpdateRequestProcessServiceImpl] (xxxMessageListenerContainer-1) [] 返回自 XXXUpdateRequestProcessServiceImpl.login() 07:51:05,775 信息 [xxx.integration.services.XXXUpdateRequestProcessServiceImpl] (xxxMessageListenerContainer-1) [] XXXUpdateRequestProcessServiceImpl.getUpdateTechToolResponseDTOs()-> 服务中的令牌:b3b00ab6-5c36-45af-8ec2-0b6f1710e888 07:51:05,777 信息 [xxx.integration.services.VehicleRepositoryService] (vrMessageListenerContainer-1) [] VehicleRepositoryService.parseVehicleRepositoryUpdate()-> 租户是 空 07:51:05,780 信息 [xxx.integration.jms.VehicleRepositoryListener] (vrMessageListenerContainer-1) [] VehicleRepositoryListener.onMessage()--> 处理成功 在 VehicleRepositoryListener 07:51:06,138 INFO 中完成 [xxx.application.VehicleActivationService] (http-/0.0.0.0:16780-71) [] VehicleActivationService.checkActivation() 内部 07:51:06,141 信息 [xxx.application.VehicleActivationService] (http-/0.0.0.0:16780-71) [] VehicleActivationService.getTenant() 内部 07:51:06,145 错误 [xxx.integration.rest.UpdateTechTool] (http-/0.0.0.0:16780-71) [] 检查激活方法中的异常:无法打开 JPA EntityManager 用于事务;嵌套异常是 javax.persistence.PersistenceException:异常 [EclipseLink-28013] (Eclipse 持久性服务 - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:无法部署 PersistenceUnit [default] in 无效状态 [DeployFailed]。内部异常: javax.persistence.PersistenceException:异常 [EclipseLink-28019] (Eclipse 持久性服务 - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:PersistenceUnit [默认] 的部署失败。 关闭此 PersistenceUnit 的所有工厂。内部异常: javax.persistence.PersistenceException:异常 [EclipseLink-28009] (Eclipse 持久性服务 - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.EntityManagerSetupException 异常描述:试图重新部署一个名为 /file:/opt/jboss64/modules/system/layers/base/org/jboss/as/ejb3/main/timers/_default 没有关闭它。 07:51:06,148 信息 [xxx.application.XXXUpdateTechToolDTOService] (http-/0.0.0.0:16780-71) [] 内部 UpdateTechToolDTOService.update() 07:51:06,154 错误 [xxx.application.XXXUpdateTechToolDTOService] (http-/0.0.0.0:16780-71) [] XXXUpdateTechToolDTOService.update() -> 调用时出现异常 更新:java.lang.NullPointerException 07:51:06,166 信息 [xxx.integration.services.UpdateRequestProcessServiceImpl] (MessageListenerContainer-1) [] 里面 UpdateRequestProcessServiceImpl.createResponse() 07:51:06,883 信息 [stdout] (Finalizer) [] [EL Info]: 连接: 2017-04-17 07:51:06.882--ServerSession(1485077057)--/file:/opt/jboss64/modules/system/layers/base/org/jboss/as/ejb3/main/timers/_default 注销成功 07:51:06,885 INF

处理并发问题的任何帮助或方法。

类似于: EclipseLink - Error: Attempted to redeploy a session without closing it

使用的 MultiTenantJpaTransactionManager 类是:

公共类 MultiTenantJpaTransactionManager 扩展 JpaTransactionManager {

TenantResolver<Tenant> tenantResolver; 
SchemaProvider schemaProvider; 
@Autowired
public void setTenantResolver(TenantResolver<Tenant> tenantResolver) {
    this.tenantResolver = tenantResolver;
}

@Autowired(required = false)
public void setSchemaProvider(SchemaProvider schemaProvider) {
    this.schemaProvider = schemaProvider;
}

@Override
protected synchronized javax.persistence.EntityManager createEntityManagerForTransaction() {
    boolean refreshed = false;
    EntityManager em = super.createEntityManagerForTransaction();
    try {
        LOGGER.info("Entering createEntityManagerforTransaction()");
        if (schemaProvider != null) {
            TenantSessionCustomizer.setSchemaName(schemaProvider.getSchema());
        }
        ServerSession ss = ((EntityManagerImpl) em.getDelegate()).getServerSession();
        Map sessionProp = ss.getProperties();
        String actualTenant = (String) sessionProp.get(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT);
        LOGGER.info("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> ACTUAL_TENANT : " + actualTenant);

        if (tenantResolver != null && tenantResolver.getCurrentTenant() != null) {
            LOGGER.info("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> CURRENT_TENANT : " + tenantResolver.getCurrentTenant());
            if ((actualTenant == null) || (actualTenant != null && !actualTenant.equals(tenantResolver.getCurrentTenant().getHandle()))) {
                sessionProp.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenantResolver.getCurrentTenant().getHandle());
                sessionProp.put(PersistenceUnitProperties.SESSION_CUSTOMIZER, TenantSessionCustomizer.class.getName());
                MetadataRefreshListener mrl = ((EntityManagerImpl) em.getDelegate()).getServerSession().getRefreshMetadataListener();
                if (mrl != null) {
                    Map<String, Object> prop = new HashMap<String, Object>();
                    mrl.triggerMetadataRefresh(prop);
                    refreshed = true;
                    LOGGER.info("createEntityManagerforTransaction()---->Metadata refreshed");
                }
            }
        }
    } catch (Exception ex) {
        LOGGER.error("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> Exception while creating EntityManagerForTransaction " + ex.getMessage());
    }
    return refreshed ? super.createEntityManagerForTransaction() : em;
}

}

【问题讨论】:

  • 看起来 Spring 正在尝试使用新凭据重新部署 EMF,而旧凭据仍在使用中,但同时提供了相同的会话名称。您需要将此标记为 Spring 问题,因为我不了解 Spring 内部结构,但它需要提供唯一的会话名称以允许使用不同的租户凭据并保持分开。
  • 嗨,克里斯,你有没有合适的例子,你使用单独的凭据而不定义持久性 xml 和单表多租户,因为我们使用的是注释驱动的 spring 4.x。我们在基于 rest api 的调用中使用基于用户 id 和密码的相同连接来确定使用 spring 数据的租户。
  • 单表多租户不需要单独的凭据 - 请参阅eclipse.org/eclipselink/documentation/2.5/solutions/… 为什么使用不同的凭据?我不确定这是如何在 Spring 中设置的,但它们需要为每组凭据配置为不同的持久性单元(如果需要)。
  • 我们没有使用单独的凭据,也没有使用 persistence.xml 或不同的 PU。
  • 这是 EL 或 Spring 中的已知问题吗?或者我们有什么替代方法?

标签: spring jpa eclipselink multi-tenant


【解决方案1】:

公共类 MultiTenantJpaTransactionManager 扩展 JpaTransactionManager { private static final Logger LOGGER = LoggerFactory.getLogger(MultiTenantJpaTransactionManager.class);

TenantResolver<Tenant> tenantResolver; 
SchemaProvider schemaProvider;

@Autowired
public void setTenantResolver(TenantResolver<Tenant> tenantResolver) {
    this.tenantResolver = tenantResolver;
}

@Autowired(required = false)
public void setSchemaProvider(SchemaProvider schemaProvider) {
    this.schemaProvider = schemaProvider;
}

@Override
protected synchronized javax.persistence.EntityManager createEntityManagerForTransaction() {
    boolean refreshed = false;
    EntityManagerFactory emf = getEntityManagerFactory();
    EntityManager em = super.createEntityManagerForTransaction();
    try {
        LOGGER.info("Entering createEntityManagerforTransaction()");
        if (schemaProvider != null) {
            TenantSessionCustomizer.setSchemaName(schemaProvider.getSchema());
        }
        ServerSession ss = ((EntityManagerImpl) em.getDelegate()).getServerSession();
        Map sessionProp = ss.getProperties();

        if (tenantResolver != null && tenantResolver.getCurrentTenant() != null) {
            sessionProp.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenantResolver.getCurrentTenant().getHandle());

            em = emf.createEntityManager(sessionProp);
        }
    } catch (Exception ex) {
        LOGGER.error("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> Exception while creating EntityManagerForTransaction " + ex.getMessage());
    }
    return em;
}

}

目前使用上面的代码进行测试似乎已经解决了这个问题。

【讨论】:

  • 我发布的上述代码运行良好。
【解决方案2】:

关注https://www.eclipse.org/eclipselink/documentation/2.5/solutions/multitenancy002.htm

这可能有效:

@Override
protected synchronized javax.persistence.EntityManager createEntityManagerForTransaction() {
    boolean refreshed = false;
    EntityManagerFactory emf = getEntityManagerFactory();
    EntityManager em = null;
    try {
        if (schemaProvider != null) {
            TenantSessionCustomizer.setSchemaName(schemaProvider.getSchema());
        }
        Map tenantProp = new HashMap();

        if (tenantResolver != null && tenantResolver.getCurrentTenant() != null) {
           tenantProp.put(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, tenantResolver.getCurrentTenant().getHandle());

             em = emf.createEntityManager(tenantProp);
        }
    } catch (Exception ex) {
        LOGGER.error("MultiTenantJpaTransactionManager.createEntityManagerForTransaction()-> Exception while creating EntityManagerForTransaction " + ex.getMessage());
    }
    return em;
}

【讨论】:

  • 在这一行: if (tenantResolver != null && tenantResolver.getCurrentTenant() != null) {sessionProp.put(xxx) "sessionProp" 你的意思是有语句行 1)ServerSession ss = ((EntityManagerImpl) em.getDelegate()).getServerSession();第 2 行)映射 sessionProp = ss.getProperties();在前一行(如果 stmt 之前)?因为方法中的 sessionProp 没有解析。请告诉我
  • 我认为您使用的是tenantProp 而不是 sessionProp 对吧?
  • Chris 在上面的代码 sn-p 中,您提供的地图tenantProp 从未为tenant-id 属性设置。是否还有意义。
  • 从您的方法和链接中剪切和粘贴错误。 sessionProp 本来应该是tenantProp,我已经更正了。
猜你喜欢
  • 2016-04-01
  • 2023-03-07
  • 1970-01-01
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-03
相关资源
最近更新 更多