【发布时间】:2014-12-22 16:31:25
【问题描述】:
我正在尝试向我的 Spring 数据 jpa 存储库添加多租户支持。我想为每个请求动态设置租户 ID,但它不适用于存储库上的自定义查找器 findBy* 方法。 我已遵循本指南:http://codecrafters.blogspot.sk/2013/03/multi-tenant-cloud-applications-with.html
我的存储库如下所示:
public interface CountryRepository extends PagingAndSortingRepository<Country, Long> {
Country findByName(String name);
Country findByIsoCountryCode(String isoCountryCode);
}
当我在存储库接口上调用任何自定义查找器 findBy* 方法时,我收到以下错误:
javax.persistence.PersistenceException: Exception [EclipseLink-6174] (Eclipse Persistence Services - 2.5.2.v20140319-9ad6abd): org.eclipse.persistence.exceptions.QueryException
Exception Description: No value was provided for the session property [eclipselink.tenant-id]. This exception is possible when using additional criteria or tenant discriminator columns without specifying the associated contextual property. These properties must be set through Entity Manager, Entity Manager Factory or persistence unit properties. If using native EclipseLink, these properties should be set directly on the session.
Query: ReadAllQuery(referenceClass=Country sql="SELECT ID, TENANT_ID, CONTINENT, CREATED_BY, CREATED_DATETIME, CURRENCY, INDEPENDENTFROM, ISOCOUNTRYCODE, LONGNAME, MODIFIED_BY, MODIFIED_DATETIME, NAME, POPULATION, REC_VERSION FROM COUNTRY WHERE ((NAME = ?) AND (TENANT_ID = ?))")
at org.eclipse.persistence.internal.jpa.QueryImpl.getSingleResult(QueryImpl.java:547)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.getSingleResult(EJBQueryImpl.java:400)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:360)
at com.sun.proxy.$Proxy56.getSingleResult(Unknown Source)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$SingleEntityExecution.doExecute(JpaQueryExecution.java:197)
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:74)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:97)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:88)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:421)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:381)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:98)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:262)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:111)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy52.findByName(Unknown Source)
我假设 spring data 在初始化阶段生成了那些自定义查找器 findBy* 方法的实现,并将它们与当前实体管理器一起放入缓存中,而没有设置租户 ID,我是无法在此缓存实体管理器上设置/更改租户 ID。我正在尝试根据请求动态更改实体管理器上的租户 ID,所以问题是如何更改/设置该缓存实体管理器上的租户 ID,当我调用任何自定义查找器 findBy* 方法。
这是我的多租户 querydsl 存储库实现:
public class MultiTenantQueryDslJpaRepository<T, ID extends Serializable> extends QueryDslJpaRepository<T, ID> {
private final CurrentTenantResolver currentTenantResolver;
protected final EntityManager entityManager;
public MultiTenantQueryDslJpaRepository(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager, CurrentTenantResolver currentTenantResolver) {
this(entityInformation, entityManager, SimpleEntityPathResolver.INSTANCE, currentTenantResolver);
}
public MultiTenantQueryDslJpaRepository(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager, EntityPathResolver resolver, CurrentTenantResolver currentTenantResolver) {
super(entityInformation, entityManager, resolver);
this.currentTenantResolver = currentTenantResolver;
this.entityManager = entityManager;
}
protected void setCurrentTenant() {
entityManager.setProperty(PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT, currentTenantResolver.getCurrentTenantId());
}
@Override
protected JPQLQuery createQuery(final Predicate... predicate) {
setCurrentTenant();
return super.createQuery(predicate);
}
@Override
public void delete(final T entity) {
setCurrentTenant();
super.delete(entity);
}
@Override
public T findOne(final ID id) {
setCurrentTenant();
return super.findOne(id);
}
@Override
public void deleteInBatch(final Iterable<T> entities) {
setCurrentTenant();
super.deleteInBatch(entities);
}
@Override
public void deleteAllInBatch() {
setCurrentTenant();
super.deleteAllInBatch();
}
@Override
public T getOne(final ID id) {
setCurrentTenant();
return super.getOne(id);
}
@Override
public boolean exists(final ID id) {
setCurrentTenant();
return super.exists(id);
}
@Override
protected TypedQuery<T> getQuery(final Specification<T> spec, final Sort sort) {
setCurrentTenant();
return super.getQuery(spec, sort);
}
@Override
public long count() {
setCurrentTenant();
return super.count();
}
@Override
protected TypedQuery<Long> getCountQuery(final Specification<T> spec) {
setCurrentTenant();
return super.getCountQuery(spec);
}
@Override
public <S extends T> S save(final S entity) {
setCurrentTenant();
return super.save(entity);
}
}
【问题讨论】:
-
你找到解决方案了吗?
-
是的。该解决方案基于 Eclipse-link 对 BindCallCustomParameter 的特定处理,该处理作为租户持有者添加到 EM 属性映射中。
标签: eclipselink spring-data-jpa multi-tenant