Indika Sampath
Indika Sampath

Reputation: 1063

Transaction support for Discriminator model in Muiti-tenancy with Spring + Hibernate

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&amp;autoReconnect=true&amp;useUnicode=true&amp;connectionCollation=utf8_general_ci&amp;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

Answers (1)

Jigar Parekh
Jigar Parekh

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

Related Questions