Multi rental with spring jpa and eclipselink data

I am trying to add multiple tenant support to my jpa data Spring data repositories. I would like to dynamically set the tenant identifier for each request, but it does not work for findBy * search methods in the repository. I followed this guide: http://codecrafters.blogspot.sk/2013/03/multi-tenant-cloud-applications-with.html

My repository is as follows:

public interface CountryRepository extends PagingAndSortingRepository<Country, Long> { Country findByName(String name); Country findByIsoCountryCode(String isoCountryCode); } 

I get an error message below when I call any findBy * search methods in the repository interface:

 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) 

I assume that the Spring data generates an implementation of these findBy * search methods at the initialization stage and puts them in the cache with the current entity manager without the tenant identifier set on it and I cannot set / change the tenant identifier in this cached entity manager. I am trying to change the tenant identifier in the entity manager dynamically for each request, so the question is how to change / set the tenant identifier in this cached entity manager, which is used when I call any findBy * user search.

Here is my implementation of the querydsl multi-user repository:

 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); } } 
+5
spring-data-jpa eclipselink multi-tenant
source share
1 answer

The solution is based on the specific eclipse-link BindCallCustomParameter processing, which is added as a tenant holder to the EM property map.

 public class TenantHolder extends BindCallCustomParameter { private final TenantResolver tenantResolver; private String defaultTenant; public TenantHolder(String defaultTenant, TenantResolver tenantResolver) { this.defaultTenant = defaultTenant; this.tenantResolver = tenantResolver; } public String getDefaultTenant() { return defaultTenant; } @Override public void set(DatabasePlatform platform, PreparedStatement statement, int index, AbstractSession session) throws SQLException { String resolvedTenant = resolveTenant(); platform.setParameterValueInDatabaseCall(resolvedTenant, statement, index, session); } private String resolveTenant() { return tenantResolver.resolveTenant(defaultTenant); } 

}

+2
source share

All Articles