Eclipse Link 多租户不工作
Eclipse Link Multitenancy not working
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 启动应用程序时,它工作得很好。为了在集成测试期间提供租户信息,我定义了一个注释:
@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 多租户不起作用。
好的,我成功了。如果有人遇到过类似的问题,这是我的解决方案。
如果使用事务,必须在事务启动后设置租户区分的上下文属性(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";
}
}
免责声明:这不会回答上述查询,但提供了替代方法。
使用字节码工具,我使用 Eclipse Link 和 Spring 数据创建了一个关于多租户(每个租户 Table)的 java 示例。选择这个想法是为了利用 Spring 数据的全部力量。
可以执行 MultiTenantTest 以查看它是否正常工作。
这个想法是开源的,可以在 Maven Central
步骤:
1.Include依赖
<dependency>
<groupId>org.bitbucket.swattu</groupId>
<artifactId>jpa-agent</artifactId>
<version>2.0.2</version>
</dependency>
2.Create一个class如下图。包,Class 和方法必须完全相同。
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.Addjava启动时代理java
-javaagent:{path-to-jpa-agent-jar}
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 启动应用程序时,它工作得很好。为了在集成测试期间提供租户信息,我定义了一个注释:
@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 多租户不起作用。
好的,我成功了。如果有人遇到过类似的问题,这是我的解决方案。
如果使用事务,必须在事务启动后设置租户区分的上下文属性(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";
}
}
免责声明:这不会回答上述查询,但提供了替代方法。
使用字节码工具,我使用 Eclipse Link 和 Spring 数据创建了一个关于多租户(每个租户 Table)的 java 示例。选择这个想法是为了利用 Spring 数据的全部力量。
可以执行 MultiTenantTest 以查看它是否正常工作。
这个想法是开源的,可以在 Maven Central
步骤:
1.Include依赖
<dependency>
<groupId>org.bitbucket.swattu</groupId>
<artifactId>jpa-agent</artifactId>
<version>2.0.2</version>
</dependency>
2.Create一个class如下图。包,Class 和方法必须完全相同。
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.Addjava启动时代理java
-javaagent:{path-to-jpa-agent-jar}