【问题标题】:Eclipse Link Multitenancy not workingEclipse 链接多租户不工作
【发布时间】:2018-08-21 11:29:12
【问题描述】:

Eclipse Link Multitenancy 无法正常工作。

示例实体(架构由 liquibase 创建):

@Entity
@Table(name = "ENTITIES")
@Multitenant(MultitenantType.SINGLE_TABLE)
@TenantDiscriminatorColumn(name = "TENANT_ID", contextProperty = "eclipselink.tenant-id")
public class EntityClass

要在实体管理器上设置多租户属性,我使用了一个方面,如下所示:

@Around("execution(* javax.persistence.EntityManagerFactory.*(..))")
public Object invocate(ProceedingJoinPoint joinPoint) throws Throwable {
    final Object result = joinPoint.proceed();

    if (result instanceof EntityManager) {
        EntityManager em = (EntityManager) result;

        final String tenantId = TenantContext.getCurrentTenantId();
        LOG.debug("Set EntityManager property for tenant {}.", tenantId);
        em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT,
                tenantId);

        return em;
    }

    return result;
}

当我启动 Spring Boot 应用程序时,它可以完美运行。为了在集成测试期间提供租户信息,我定义了一个注释:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AsTenant {
    String value();
}

为了绑定这个值,我使用了TestExecutionListener

@Override
public void beforeTestMethod(TestContext testContext) throws Exception {
    final Method testMethod = testContext.getTestMethod();
    final AsTenant asTenantAnnotation = testMethod
            .getAnnotation(AsTenant.class);

    if (asTenantAnnotation != null) {
        TenantContext.setCurrentTenantId(asTenantAnnotation.value());
    }
}

通过调试,我可以清楚地说TestExectionListener 在创建任何 EM 之前被调用,并且该属性已为 EM 正确设置。将任何内容持久化到数据库时,Eclipse Link 不会为该列设置值。

也许任何人都可以帮助我解决这个问题,我不知道为什么 EclipseLink 多租户不起作用。

【问题讨论】:

    标签: spring-boot eclipselink multi-tenant spring-test


    【解决方案1】:

    好的,我搞定了。如果有人遇到过类似的问题,这是我的解决方案。

    如果使用事务,则必须在事务开始后设置租户区分的上下文属性 (http://www.eclipse.org/eclipselink/documentation/2.5/solutions/multitenancy002.htm)。

    EntityManager em = createEntityManager(MULTI_TENANT_PU);
    em.getTransaction().begin();
    em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, "my_id");
    

    为了在 Spring Boot/Data 环境中实现这一点,我自定义了 Spring 的JpaTransactionManager。这仍然是问题中的方面的补充,因为SELECT 查询没有事务。

    public class MultitenantJpaTransactionManager extends JpaTransactionManager {
    
        /* (non-Javadoc)
         * @see org.springframework.orm.jpa.JpaTransactionManager#doBegin(java.lang.Object, org.springframework.transaction.TransactionDefinition)
         */
        @Override
        protected void doBegin(Object transaction, TransactionDefinition definition) {
            super.doBegin(transaction, definition);
    
            final EntityManagerHolder emHolder = (EntityManagerHolder) TransactionSynchronizationManager.getResource(getEntityManagerFactory());
            final EntityManager em = emHolder.getEntityManager();
            final String tenantId = TenantContext.getCurrentTenantId();
    
            if (tenantId != null) {
                em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, tenantId);
            }
        }
    }
    

    这很容易通过 JpaConfiguration 进行连接:

    /**
     * Configures Eclipse Link as JPA Provider.
     */
    @Configuration
    @EnableTransactionManagement
    @AutoConfigureAfter({ DataSourceAutoConfiguration.class })
    public class JpaConfiguration extends JpaBaseConfiguration {
    
        @Bean
        @Override
        public PlatformTransactionManager transactionManager() {
            return new MultitenantJpaTransactionManager();
        }
    
        @Override
        protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
            EclipseLinkJpaVendorAdapter adapter = new EclipseLinkJpaVendorAdapter();
            return adapter;
        }
    
        @Override
        protected Map<String, Object> getVendorProperties() {
            HashMap<String, Object> properties = new HashMap<String, Object>();
    
            properties.put(PersistenceUnitProperties.WEAVING, detectWeavingMode());
    
            return properties;
        }
    
        private String detectWeavingMode() {
            return InstrumentationLoadTimeWeaver.isInstrumentationAvailable()
                ? "true" : "static";
        }
    }
    

    【讨论】:

      【解决方案2】:

      免责声明:这不回答上述查询,但提供了替代方案。

      使用字节码检测,我使用 Eclipse Link 和 Spring Data 创建了一个关于多租户(每个租户的表)的 java 示例。选择这个想法是为了充分利用 Spring Data 的强大功能。

      可以执行MultiTenantTest 来查看它的工作情况。

      这个想法是开源的,可以在Maven Central获得

      步骤:

      1.包含依赖

      <dependency>
          <groupId>org.bitbucket.swattu</groupId>
          <artifactId>jpa-agent</artifactId>
          <version>2.0.2</version>
      </dependency>
      

      2.创建一个类,如下所示。包、类和方法必须完全相同。

      package org.swat.jpa.base;
      import javax.persistence.EntityManager;
      public class EntityManagerFactoryListener {
          /**
           * This method is called by JPA Agent.
           *
           * @param entityManager the entity manager
           */
          public static void afterCreateEntityManager(EntityManager entityManager) {
              //Business logic to set appropriate values in entityManager
              final String tenantId = TenantContext.getCurrentTenantId();
              if (tenantId != null) {
                  em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, tenantId);
              }       
          }
      }
      

      3.启动java时添加javaagent

      -javaagent:{path-to-jpa-agent-jar}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-06-03
        • 2021-04-22
        • 1970-01-01
        • 2018-05-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多