Reputation: 1063
Our current development based on Discriminator model in Multi-tenancy. Following is the technical stack we are currently engage with,
We are maintain tenant id by keep one column separately in each table. Tenant id filter when session is created.
Example model class.
@Entity
@FilterDef(name = "tenantFilter", parameters = @ParamDef(name = "tenantIdParam", type = "string"))
@Filters(@Filter(name = "tenantFilter", condition = "tenant_id = :tenantIdParam"))
@Table(name = "assessment")
public class Assessment implements java.io.Serializable, Comparable<Assessment> {
private static final long serialVersionUID = -2231966582754667029L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Long id;
@Column(name = "tenant_id", nullable = false, updatable = false)
private String tenantId;
// rest of the code...
}
This is the configuration of session factory
<!-- create database connection pool -->
<bean id="dataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="jdbc:mysql://${jdbc.host}:3306/${jdbc.database}?createDatabaseIfNotExist=true&autoReconnect=true&useUnicode=true&connectionCollation=utf8_general_ci&characterEncoding=UTF-8" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="idleConnectionTestPeriodInMinutes" value="60"/>
<property name="idleMaxAgeInMinutes" value="240"/>
<property name="maxConnectionsPerPartition" value="30"/>
<property name="minConnectionsPerPartition" value="10"/>
<property name="partitionCount" value="3"/>
<property name="acquireIncrement" value="5"/>
<property name="statementsCacheSize" value="100"/>
<property name="releaseHelperThreads" value="3"/>
</bean>
<!-- Hibernate SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan" value="lk.gov.elg.orm.model"/>
<property name="hibernateProperties">
<value>
hibernate.dialect=${hibernate.dialect}
hibernate.hbm2ddl.auto=update
</value>
</property>
</bean>
<bean id="tenantBasedSessionFactory" class="lk.gov.elg.orm.dao.impl.TenantBasedSessionFactoryImpl">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
Tenant base session factory
public class TenantBasedSessionFactoryImpl implements TenantBasedSessionFactory {
private SessionFactory sessionFactory;
@Override
public Session getTenantBasedSession(Object tenantId) {
Session session = sessionFactory.openSession();
session.enableFilter("tenantFilter").setParameter("tenantIdParam", tenantId);
return session;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Session getAllTenantBasedSession() {
Session session = sessionFactory.openSession();
return session;
}
}
sample Service class
@Service("assessmentService")
public class AssessmentServiceImpl implements AssessmentService {
@Autowired
private AssessmentDao assessmentDao;
public Long saveAssessment(Assessment assessment, Object tenantId) {
return assessmentDao.saveAssessment(assessment, tenantId);
}
}
Sample DAO class
@Repository("assessmentDao")
public class AssessmentDaoImpl implements AssessmentDao {
@Autowired
private TenantBasedSessionFactory tenantBasedSessionFactory;
public Long saveAssessment(Assessment assessment, Object tenantId) {
Session session = tenantBasedSessionFactory.getTenantBasedSession(tenantId);
try {
session.beginTransaction();
session.save(assessment);
session.getTransaction().commit();
return assessment.getId();
} catch (HibernateException e) {
logger.error("Error in persist assessment:", e);
session.getTransaction().rollback();
return null;
} finally {
session.close();
}
}
}
I would like to know is there a way to get the spring transaction support with this Discriminator model for database transactions ? And the other thing is I would like to know is there any advantage of give transaction handling to spring rather than handling it by our side?
Thanks in advance.
Upvotes: 3
Views: 2055
Reputation: 6283
i had similar problem and i have resolved it using aspect instead of customizing sessionfactory, so i can leverage annotation driven transaction support
Below code is for aspect which works on annotation @Tennant
@Aspect
public class TennantAspect {
@Autowired
private SessionFactory sessionFactory;
@Around("@annotation(Tennant)")
public Object enableClientFilter(ProceedingJoinPoint pjp) throws Throwable {
Object obj;
boolean isDAO=(pjp.getTarget() instanceof BaseDAO<?,?>);
try {
if(isDAO){
Authentication auth=SecurityContextHolder.getContext().getAuthentication();
if(auth!=null){
User user=(User) auth.getPrincipal();
this.sessionFactory.getCurrentSession().enableFilter("clientFilter").setParameter("clientId", user.getClientId());
}
}
obj=pjp.proceed();
}finally {
if(isDAO)
this.sessionFactory.getCurrentSession().disableFilter("clientFilter");
}
return obj;
}
}
Hope this solves your problem.
Alternatively you can also look at tenancy support by hiberante & spring
http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch16.html https://github.com/mariofts/spring-multitenancy
Upvotes: 1