Skip to content

Commit ec712af

Browse files
committed
First commmit for Spring Multi-tenancy with Hibernate example.
1 parent df2f1cb commit ec712af

File tree

7 files changed

+281
-0
lines changed

7 files changed

+281
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.baeldung.multitenancy.entities;
2+
3+
import javax.persistence.*;
4+
import javax.validation.constraints.NotNull;
5+
6+
@Entity
7+
public class Book {
8+
9+
@Id
10+
@GeneratedValue(strategy = GenerationType.AUTO)
11+
@Column(name = "id")
12+
Long id;
13+
14+
@NotNull
15+
@Column(name = "name")
16+
String name;
17+
18+
// standard getters and setters
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.baeldung.multitenancy.implementation;
2+
3+
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
8+
import javax.servlet.http.HttpServletRequest;
9+
import java.text.MessageFormat;
10+
11+
/**
12+
* CurrentSessionTenantIdentifierResolver-- here we fetch the tenantId from the request.
13+
* User: baeldung
14+
* Date: 9/06/15
15+
*/
16+
17+
public class CurrentSessionTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
18+
19+
@Autowired
20+
private HttpServletRequest request;
21+
22+
@Override
23+
public String resolveCurrentTenantIdentifier() {
24+
String tenantId = request.getHeader("X-TenantId");
25+
return tenantId;
26+
}
27+
28+
@Override
29+
public boolean validateExistingCurrentSessions() {
30+
// Additional logic to ensure appropriate connections are made.
31+
return false;
32+
}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.baeldung.multitenancy.implementation;
2+
3+
import org.hibernate.tool.hbm2ddl.SchemaExport;
4+
5+
import javax.sql.DataSource;
6+
import java.io.IOException;
7+
import java.sql.Connection;
8+
import java.sql.SQLException;
9+
import java.util.Map;
10+
11+
/**
12+
* Created with IntelliJ IDEA.
13+
* User: vtajzich
14+
* Date: 8/21/13
15+
*/
16+
public class SessionFactoryBean extends org.springframework.orm.hibernate4.LocalSessionFactoryBean {
17+
18+
private Map<String, DataSource> dataSourceMap;
19+
20+
@Override
21+
public void afterPropertiesSet() throws IOException {
22+
super.afterPropertiesSet();
23+
24+
for (Map.Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
25+
26+
Connection connection = null;
27+
28+
try {
29+
30+
connection = entry.getValue().getConnection();
31+
32+
SchemaExport export = new SchemaExport(getConfiguration(), connection);
33+
34+
export.setOutputFile(entry.getKey() + "-schema.sql");
35+
export.setDelimiter(";");
36+
37+
// define your rules, here.
38+
//The below code allows creation, but does not allow dropping entries
39+
export.execute(true, true, false, true);
40+
41+
} catch (SQLException e) {
42+
throw new RuntimeException(e);
43+
}
44+
}
45+
}
46+
47+
public void setDataSourceMap(Map<String, DataSource> dataSourceMap) {
48+
this.dataSourceMap = dataSourceMap;
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.baeldung.multitenancy.implementation;
2+
3+
import org.hibernate.service.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl;
4+
5+
import javax.sql.DataSource;
6+
import java.util.Map;
7+
8+
/**
9+
* CurrentSessionTenantIdentifierResolver-- here we fetch the DB conection for the given tenantIdentifier.
10+
* User: baeldung
11+
* Date: 9/06/15
12+
*/
13+
14+
public class SimpleMultiTenantConnectionProvider extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl {
15+
16+
private Map<String, DataSource> dataSourceMap;
17+
18+
@Override
19+
protected DataSource selectAnyDataSource() {
20+
return (DataSource) dataSourceMap.values().toArray()[0];
21+
}
22+
23+
@Override
24+
protected DataSource selectDataSource(String tenantIdentifier) {
25+
return dataSourceMap.get(tenantIdentifier);
26+
}
27+
28+
public Map<String, DataSource> getDataSourceMap() {
29+
return dataSourceMap;
30+
}
31+
32+
public void setDataSourceMap(Map<String, DataSource> dataSourceMap) {
33+
this.dataSourceMap = dataSourceMap;
34+
}
35+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.baeldung.multitenancy.service;
2+
3+
import com.baeldung.multitenancy.entities.Book;
4+
import org.hibernate.SessionFactory;
5+
import org.springframework.stereotype.Service;
6+
7+
import java.util.List;
8+
9+
/**
10+
* MultitenantService-- we use the SessionFactory to save and fetch entities.
11+
* User: baeldung
12+
* Date: 9/06/15
13+
*/
14+
15+
@Service
16+
public class MultiTenantService {
17+
18+
@AutoWired
19+
private SessionFactory sessionFactory;
20+
21+
public void save(Book book) {
22+
sessionFactory.getCurrentSession().save(book);
23+
}
24+
25+
public Book findBook(Long id) {
26+
return (Book) sessionFactory.
27+
getCurrentSession().get(Book.class, id);
28+
}
29+
30+
public List<Book> findAllBooks() {
31+
return sessionFactory.
32+
getCurrentSession().createQuery("from Books").list();
33+
}
34+
35+
public void setSessionFactory(SessionFactory sessionFactory){
36+
this.sessionFactory = sessionFactory;
37+
}
38+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2+
<beans xmlns="http://www.springframework.org/schema/beans"
3+
xmlns:context="http://www.springframework.org/schema/context"
4+
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
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">
6+
7+
<context:property-placeholder location="classpath*:database.properties" />
8+
<context:spring-configured />
9+
10+
<context:component-scan base-package="com.baeldung.multitenancy">
11+
</context:component-scan>
12+
13+
<util:map id="mydatasources">
14+
<entry key="tenant1" value-ref="source1" />
15+
<entry key="tenant2" value-ref="source2" />
16+
</util:map>
17+
18+
<bean id="source1" parent="mainDataSource">
19+
<property name="url" value="jdbc:mysql://localhost/tenant1" />
20+
<property name="username" value="baeldung" />
21+
<property name="password" value="spring" />
22+
</bean>
23+
24+
<bean id="source2" parent="mainDataSource">
25+
<property name="url" value="jdbc:mysql://localhost/tenant2" />
26+
<property name="username" value="testuser" />
27+
<property name="password" value="password" />
28+
</bean>
29+
30+
<bean class="org.apache.commons.dbcp.BasicDataSource" abstract="true"
31+
destroy-method="close" id="mainDataSource">
32+
<property name="driverClassName" value="${database.driverClassName}" />
33+
<property name="testOnBorrow" value="true" />
34+
<property name="testOnReturn" value="true" />
35+
<property name="testWhileIdle" value="true" />
36+
<property name="numTestsPerEvictionRun" value="2" />
37+
<property name="minEvictableIdleTimeMillis" value="1000000" />
38+
<property name="timeBetweenEvictionRunsMillis" value="1000000" />
39+
<property name="validationQuery" value="SELECT 1" />
40+
</bean>
41+
42+
<bean id="sessionFactory" class="com.baeldung.multitenancy.SessionFactoryBean">
43+
<property name="dataSourceMap" ref="mydatasources" />
44+
<property name="dataSource" ref="source1" />
45+
<property name="hibernateProperties">
46+
<map>
47+
<entry key="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
48+
<entry key="hibernate.show_sql" value="true" />
49+
<entry key="hibernate.multiTenancy" value="DATABASE" />
50+
<entry key="hibernate.tenant_identifier_resolver"
51+
value-ref="currentSessionTenantIdentifierResolver" />
52+
<entry key="hibernate.multi_tenant_connection_provider"
53+
value-ref="simpleMultiTenantConnectionProvider" />
54+
</map>
55+
</property>
56+
</bean>
57+
58+
<bean id="currentSessionTenantIdentifierResolver"
59+
class="com.baeldung.multitenancy.implementation.CurrentSessionTenantIdentifierResolver"
60+
scope="request">
61+
<aop:scoped-proxy />
62+
</bean>
63+
64+
<bean id="simpleMultiTenantConnectionProvider"
65+
class="com.baeldung.multitenancy.implementation.SimpleMultiTenantConnectionProvider">
66+
<property name="dataSourceMap" ref="datasources" />
67+
</bean>
68+
69+
70+
<bean id="multitenantService" class="com.baeldung.multitenancy.service.MultitenantService">
71+
<property name="sessionFactory" ref="sessionFactory" />
72+
</bean>
73+
74+
<tx:annotation-driven mode="aspectj"
75+
transaction-manager="transactionManager" />
76+
77+
<bean id="transactionManager"
78+
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
79+
<property name="sessionFactory" ref="sessionFactory" />
80+
<property name="autodetectDataSource" value="false" />
81+
</bean>
82+
83+
<tx:advice id="transactionServiceDao" transaction-manager="transactionManager">
84+
<tx:attributes>
85+
<tx:method name="exists" propagation="SUPPORTS"
86+
rollback-for="java.lang.Exception" />
87+
<tx:method name="find*" propagation="SUPPORTS"
88+
rollback-for="java.lang.Exception" />
89+
<tx:method name="get*" propagation="SUPPORTS"
90+
rollback-for="java.lang.Exception" />
91+
<tx:method name="*" propagation="REQUIRED"
92+
rollback-for="java.lang.Exception" />
93+
<tx:method name="*Commit" propagation="REQUIRES_NEW"
94+
rollback-for="java.lang.Exception" />
95+
</tx:attributes>
96+
</tx:advice>
97+
98+
<aop:config>
99+
<aop:pointcut id="serviceInvocation"
100+
expression="execution(public com.baeldung.multitenancy.service.MultitenantService.*(..))" />
101+
<aop:advisor pointcut-ref="serviceInvocation"
102+
advice-ref="transactionServiceDao" />
103+
</aop:config>
104+
105+
</beans>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
database.driverClassName=com.mysql.jdbc.Driver

0 commit comments

Comments
 (0)