First commmit for Spring Multi-tenancy with Hibernate example.
This commit is contained in:
parent
df2f1cbaf8
commit
ec712af5f0
@ -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
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
@ -0,0 +1 @@
|
|||||||
|
database.driverClassName=com.mysql.jdbc.Driver
|
Loading…
x
Reference in New Issue
Block a user