2014-10-27 28 views
5

Sto cercando di aggiungere il supporto multi-tenancy ai miei archivi jpa di dati Spring. Vorrei impostare dinamicamente l'ID tenant per richiesta, ma non funziona con il metodo finder personalizzato findBy * sul repository. Ho seguito questa guida: http://codecrafters.blogspot.sk/2013/03/multi-tenant-cloud-applications-with.htmlMulti tenancy con dati di primavera jpa e eclipselink

mio repository assomiglia a questo:

public interface CountryRepository extends PagingAndSortingRepository<Country, Long> { 

    Country findByName(String name); 
    Country findByIsoCountryCode(String isoCountryCode); 

} 

sto ottenendo l'errore sotto quando chiamo una delle finder personalizzato findBy * metodi sulla interfaccia repository:

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) 

suppongo che i dati di primavera genera l'attuazione di quelli finder personalizzato findBy * metodi nella fase di inizializzazione e metterli in una cache con il gestore di entità corrente senza un ID titolare impostato su di esso e non sono in grado di impostare/modificare l'ID titolare su questo gestore entità memorizzato nella cache. Sto cercando di modificare l'ID tenant sul gestore di entità in modo dinamico per richiesta, quindi la domanda è come posso modificare/impostare l'ID tenant su quel gestore di entità cache, che viene utilizzato quando chiamo uno dei finder personalizzati findBy * Metodi.

Ecco il mio multi-tenant implementazione repository 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); 
} 
} 
+0

hai trovato una soluzione per questo? –

+0

Sì. La soluzione si basa sulla gestione specifica di eclipse-link di BindCallCustomParameter che viene aggiunto come titolare del titolare alla mappa delle proprietà EM. – Ati

risposta

2

La soluzione si basa su Eclipse-link Trattamento specifico di BindCallCustomParameter che viene aggiunto come titolare inquilino a EM mappa delle proprietà.

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

}

+0

Quindi questo è tutto ciò che serve per lavorare con la multi-tenancy con Eclipselink e Spring Data? Non c'è più bisogno di archivi personalizzati? –

+0

Puoi impostare un ID tentante con repository personalizzati per richiesta? – Ati

+1

Puoi condividere, quale proprietà EM hai usato? – Ljudevit

Problemi correlati