Eclipse Link Multitenancy无效

时间:2016-04-08 13:19:16

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

Eclipse Link多租户无法正常运行。

示例实体(模式由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());
    }
}

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

也许有人可以帮我解决这个问题,我不知道为什么EclipseLink Multitenancy无效。

2 个答案:

答案 0 :(得分:0)

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

如果使用交易,则必须在交易开始后设置租户歧视的上下文属性(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。这是问题中Aspect的补充,因为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";
    }
}

答案 1 :(得分:-1)

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

我使用字节码工具在Eclipse Link和Spring Data的Multi-Tenancy(每个租户表)上创建了一个Java示例。选择这个想法是为了利用Spring Data的全部功能。

一个人可以执行MultiTenantTest来查看其工作情况。

该想法是开源的,可在Maven Central上获得

步骤:

1.Include依赖项

<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}