First commmit for Spring Multi-tenancy with Hibernate example.

This commit is contained in:
Abhi Bavishi 2015-06-12 12:43:10 +05:30
parent df2f1cbaf8
commit ec712af5f0
7 changed files with 281 additions and 0 deletions

View File

@ -0,0 +1,19 @@
package com.baeldung.multitenancy.entities;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
Long id;
@NotNull
@Column(name = "name")
String name;
// standard getters and setters
}

View File

@ -0,0 +1,33 @@
package com.baeldung.multitenancy.implementation;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
/**
* CurrentSessionTenantIdentifierResolver-- here we fetch the tenantId from the request.
* User: baeldung
* Date: 9/06/15
*/
public class CurrentSessionTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
@Autowired
private HttpServletRequest request;
@Override
public String resolveCurrentTenantIdentifier() {
String tenantId = request.getHeader("X-TenantId");
return tenantId;
}
@Override
public boolean validateExistingCurrentSessions() {
// Additional logic to ensure appropriate connections are made.
return false;
}
}

View File

@ -0,0 +1,50 @@
package com.baeldung.multitenancy.implementation;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Map;
/**
* Created with IntelliJ IDEA.
* User: vtajzich
* Date: 8/21/13
*/
public class SessionFactoryBean extends org.springframework.orm.hibernate4.LocalSessionFactoryBean {
private Map<String, DataSource> dataSourceMap;
@Override
public void afterPropertiesSet() throws IOException {
super.afterPropertiesSet();
for (Map.Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
Connection connection = null;
try {
connection = entry.getValue().getConnection();
SchemaExport export = new SchemaExport(getConfiguration(), connection);
export.setOutputFile(entry.getKey() + "-schema.sql");
export.setDelimiter(";");
// define your rules, here.
//The below code allows creation, but does not allow dropping entries
export.execute(true, true, false, true);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
public void setDataSourceMap(Map<String, DataSource> dataSourceMap) {
this.dataSourceMap = dataSourceMap;
}
}

View File

@ -0,0 +1,35 @@
package com.baeldung.multitenancy.implementation;
import org.hibernate.service.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl;
import javax.sql.DataSource;
import java.util.Map;
/**
* CurrentSessionTenantIdentifierResolver-- here we fetch the DB conection for the given tenantIdentifier.
* User: baeldung
* Date: 9/06/15
*/
public class SimpleMultiTenantConnectionProvider extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {
private Map<String, DataSource> dataSourceMap;
@Override
protected DataSource selectAnyDataSource() {
return (DataSource) dataSourceMap.values().toArray()[0];
}
@Override
protected DataSource selectDataSource(String tenantIdentifier) {
return dataSourceMap.get(tenantIdentifier);
}
public Map<String, DataSource> getDataSourceMap() {
return dataSourceMap;
}
public void setDataSourceMap(Map<String, DataSource> dataSourceMap) {
this.dataSourceMap = dataSourceMap;
}
}

View File

@ -0,0 +1,38 @@
package com.baeldung.multitenancy.service;
import com.baeldung.multitenancy.entities.Book;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* MultitenantService-- we use the SessionFactory to save and fetch entities.
* User: baeldung
* Date: 9/06/15
*/
@Service
public class MultiTenantService {
@AutoWired
private SessionFactory sessionFactory;
public void save(Book book) {
sessionFactory.getCurrentSession().save(book);
}
public Book findBook(Long id) {
return (Book) sessionFactory.
getCurrentSession().get(Book.class, id);
}
public List<Book> findAllBooks() {
return sessionFactory.
getCurrentSession().createQuery("from Books").list();
}
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
}

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
<context:property-placeholder location="classpath*:database.properties" />
<context:spring-configured />
<context:component-scan base-package="com.baeldung.multitenancy">
</context:component-scan>
<util:map id="mydatasources">
<entry key="tenant1" value-ref="source1" />
<entry key="tenant2" value-ref="source2" />
</util:map>
<bean id="source1" parent="mainDataSource">
<property name="url" value="jdbc:mysql://localhost/tenant1" />
<property name="username" value="baeldung" />
<property name="password" value="spring" />
</bean>
<bean id="source2" parent="mainDataSource">
<property name="url" value="jdbc:mysql://localhost/tenant2" />
<property name="username" value="testuser" />
<property name="password" value="password" />
</bean>
<bean class="org.apache.commons.dbcp.BasicDataSource" abstract="true"
destroy-method="close" id="mainDataSource">
<property name="driverClassName" value="${database.driverClassName}" />
<property name="testOnBorrow" value="true" />
<property name="testOnReturn" value="true" />
<property name="testWhileIdle" value="true" />
<property name="numTestsPerEvictionRun" value="2" />
<property name="minEvictableIdleTimeMillis" value="1000000" />
<property name="timeBetweenEvictionRunsMillis" value="1000000" />
<property name="validationQuery" value="SELECT 1" />
</bean>
<bean id="sessionFactory" class="com.baeldung.multitenancy.SessionFactoryBean">
<property name="dataSourceMap" ref="mydatasources" />
<property name="dataSource" ref="source1" />
<property name="hibernateProperties">
<map>
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<entry key="hibernate.show_sql" value="true" />
<entry key="hibernate.multiTenancy" value="DATABASE" />
<entry key="hibernate.tenant_identifier_resolver"
value-ref="currentSessionTenantIdentifierResolver" />
<entry key="hibernate.multi_tenant_connection_provider"
value-ref="simpleMultiTenantConnectionProvider" />
</map>
</property>
</bean>
<bean id="currentSessionTenantIdentifierResolver"
class="com.baeldung.multitenancy.implementation.CurrentSessionTenantIdentifierResolver"
scope="request">
<aop:scoped-proxy />
</bean>
<bean id="simpleMultiTenantConnectionProvider"
class="com.baeldung.multitenancy.implementation.SimpleMultiTenantConnectionProvider">
<property name="dataSourceMap" ref="datasources" />
</bean>
<bean id="multitenantService" class="com.baeldung.multitenancy.service.MultitenantService">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven mode="aspectj"
transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="autodetectDataSource" value="false" />
</bean>
<tx:advice id="transactionServiceDao" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="exists" propagation="SUPPORTS"
rollback-for="java.lang.Exception" />
<tx:method name="find*" propagation="SUPPORTS"
rollback-for="java.lang.Exception" />
<tx:method name="get*" propagation="SUPPORTS"
rollback-for="java.lang.Exception" />
<tx:method name="*" propagation="REQUIRED"
rollback-for="java.lang.Exception" />
<tx:method name="*Commit" propagation="REQUIRES_NEW"
rollback-for="java.lang.Exception" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="serviceInvocation"
expression="execution(public com.baeldung.multitenancy.service.MultitenantService.*(..))" />
<aop:advisor pointcut-ref="serviceInvocation"
advice-ref="transactionServiceDao" />
</aop:config>
</beans>

View File

@ -0,0 +1 @@
database.driverClassName=com.mysql.jdbc.Driver