Hello I'm developing a web application using Vaadin framework, JPA EclipseLink as ORM, MYSQL as database. Currently I'm working to implement Multitenant structure for my app. Here I've to choose TABLE_PER_TENANT with different schema in a shared database strategy as I already have some tenants.
Here is an example of my tenant specific entity:
@Entity
@Multitenant(MultitenantType.TABLE_PER_TENANT)
@TenantTableDiscriminator(type = TenantTableDiscriminatorType.SCHEMA, contextProperty = PersistenceUnitProperties.MULTITENANT_PROPERTY_DEFAULT)
public class UserAccount implements Serializable {
......
}
Here is my persistence unit for tenant in persistence.xml :
<persistence-unit name="PU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>Includes all tenant table class</class>
<properties>
<property name="eclipselink.cache.shared.default" value="false"/>
<!-- container isn upcloud ??-->
<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/?rewriteBatchedStatements=true&characterEncoding=UTF-8&connectionCollation=utf8_general_ci&zeroDateTimeBehavior=convertToNull&useUnicode=true&connectionCollation=utf8_general_ci&characterSetResults=utf8&characterEncoding=utf8&characterEncoding=UTF-8&characterSetResults=UTF-8"/>
<property name="javax.persistence.jdbc.user" value="root"/>
<property name="javax.persistence.jdbc.password" value="root"/>
<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
<property name="eclipselink.ddl-generation" value="create-or-extend-tables"/>
<property name="eclipselink.jdbc.batch-writing" value="JDBC"/>
<property name="eclipselink.jdbc.batch-writing.size" value="1000"/>
<property name="hibernate.connection.useUnicode" value="true"/>
<property name="hibernate.connection.characterEncoding" value="UTF-8"/>
</properties>
</persistence-unit>
Now I'm getting an entitymanager like this:
public static EntityManager createTenantSpecificEntityManager(){
EntityManager em = Persistence.createEntityManagerFactory("PU").createEntityManager(getProperties());
return em;
}
private static Map<String, Object> getProperties(){
Map<String, Object> properties = new HashMap<>();
properties.put("javax.persistence.jdbc.url", getDataBaseConnectionURL(COMPANY_NAME_AS_TENENT_ID));
return properties;
}
public static String getDataBaseConnectionURL(String schemaName){
String str = "jdbc:mysql://localhost:3306/?rewriteBatchedStatements=true&characterEncoding=UTF-8&connectionCollation=utf8_general_ci&zeroDateTimeBehavior=convertToNull&useUnicode=true&connectionCollation=utf8_general_ci&characterSetResults=utf8&characterEncoding=utf8&characterEncoding=UTF-8&characterSetResults=UTF-8";//for testing purpose
StringBuilder sb = new StringBuilder(str);
sb.insert(sb.indexOf("?"), schemaName);
return sb.toString();
}
Now I'm using entitymanager like this:
em = createTenantSpecificEntityManager();
em.getTransaction().begin();
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, \COMPANY_NAME_AS_TENENT_ID);
.......
Do any operation here
.......
em.getTransaction().commit();
em.close();
In the app user can log in to a specific tenant (where he has given the access). So at a time only one tenant's data can be accessed.
Is it the right approach for my app to make it Multi-tenant? Is there any improvement I can do?
One can use ThreadLocal as shown below:
public class TenantContext {
private static final ThreadLocal<String> TENANT_TL = new ThreadLocal<>();
public static String getTenantId() {
return TENANT_TL.get();
}
public static void setTenantId(String tenantId) {
TENANT_TL.set(tenantId);
}
}
The tenantId can be set on authentication, usually in an Interceptor. While creating EntityManager, above tenant can be used as shown:
em.setProperty(EntityManagerProperties.MULTITENANT_PROPERTY_DEFAULT, TenantContext.getTenantId());
I have created a java example on Multi-Tenancy (Table per Tenant) using Eclipse Link and Spring Data. An existing application can be converted to Multi-Tenant with a minimal code change. This is done using bytecode instrumentation. This idea is chosen to utilize the complete power of Spring Data.
One can execute MultiTenantTest to see it working.
The idea is open-sourced and is available at Maven Central
来源:https://stackoverflow.com/questions/43492375/what-is-the-right-approach-for-multi-tenancy-using-eclipselink