hibernatemultitenancy-2011-02-16

12
Multi-tenancy in Hibernate | Steve Ebersole Multi-tenancy in Hibernate Steve Ebersole Hibernate Project Lead

Upload: joao-paulo

Post on 22-Feb-2015

191 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Multi-tenancy in HibernateSteve EbersoleHibernate Project Lead

Page 2: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Agenda● What is multi­tenancy● Current options in Hibernate● Support in Hibernate 4● Q & A

Page 3: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Separate SchemaApplication

CUSTOMER (    ID BIGINT,    NAME VARCHAR,    ...)

CUSTOMER (    ID BIGINT,    NAME VARCHAR,    ...)

Page 4: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

DiscriminatorApplication

CUSTOMER (    ID BIGINT,    NAME VARCHAR,    ...    TENANT_ID VARCHAR)

Page 5: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Current Options ­ Discriminator● Hibernate filters● This approach can still leverage 2nd level cache● Application must be aware of tenant id

– Entity must expose tenant­id attribute

– Application must set “tenant id” on creation

Page 6: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Discriminator ­ Entity@Entity@FilterDef( name=”tenantFilter”, parameters=@ParamDef( name="tenantId", type="string" ) )@Filters(    @Filter( name=”tenantFilter”, condition=”tenant_id = :tenantId” ))public class Customer {    @Id     private Long id;    @Column( name=”tenant_id”, nullable=false, updateable=false )    private String tenantId;    ...}

@Entity@FilterDef( name=”tenantFilter”, parameters=@ParamDef( name="tenantId", type="string" ) )@Filters(    @Filter( name=”tenantFilter”, condition=”tenant_id = :tenantId” ))public class Customer {    @Id     private Long id;    @Column( name=”tenant_id”, nullable=false, updateable=false )    private String tenantId;    ...}

Page 7: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Discriminator ­ Usagepublic void demonstrateUsageInDiscriminatorScenario() {    // One thing that must be done on all sessions is to enable the tenant­based Hibernate filter     // we defined before on the entity.  This is usually best handled by some form of “request    // interceptor” (HttpServletRequestFilter, etc) to make sure it is done uniformly

    // Creating an entity    Session session = openSession();    session.enableFilter( “tenantFilter” ).setParameter( “tenantId”, “some­tenant­identifier” );    session.beginTransaction();    Customer customer = new Customer();    ...    customer.setTenantId( “some­tenant­identifier” );    session.persist( customer );    session.getTransaction().commit();    session.close();

    // Querying Customers    session = openSession();    session.enableFilter( “tenantFilter” ).setParameter( “tenantId”, “some­tenant­identifier” );    session.beginTransaction();    Customer customer = (Customer) session.createQuery( “from Customer” ).uniqueResult();    session.getTransaction().commit();    session.close();}

public void demonstrateUsageInDiscriminatorScenario() {    // One thing that must be done on all sessions is to enable the tenant­based Hibernate filter     // we defined before on the entity.  This is usually best handled by some form of “request    // interceptor” (HttpServletRequestFilter, etc) to make sure it is done uniformly

    // Creating an entity    Session session = openSession();    session.enableFilter( “tenantFilter” ).setParameter( “tenantId”, “some­tenant­identifier” );    session.beginTransaction();    Customer customer = new Customer();    ...    customer.setTenantId( “some­tenant­identifier” );    session.persist( customer );    session.getTransaction().commit();    session.close();

    // Querying Customers    session = openSession();    session.enableFilter( “tenantFilter” ).setParameter( “tenantId”, “some­tenant­identifier” );    session.beginTransaction();    Customer customer = (Customer) session.createQuery( “from Customer” ).uniqueResult();    session.getTransaction().commit();    session.close();}

Page 8: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Current Options – Separate Schema

● Custom ConnectionProvider to route calls to the correct JDBC connection

● Not safe with 2nd level cache● Application not aware of tenant­id

Page 9: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Separate Schema ­ Entity@Entitypublic class Customer {    @Id     private Long id;    ...}

@Entitypublic class Customer {    @Id     private Long id;    ...}

Page 10: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Separate Schema ­ ConnectionProviderpublic class MyTenantAwareConnectionProvider implements ConnectionProvider {    public static final String BASE_JNDI_NAME = "java:/comp/env/jdbc/";

    public Connection getConnection() throws SQLException {        final String tenantId = TenantContext.getTenantId()        final String tenantDataSourceName = BASE_JNDI_NAME + tenantId;        DataSource tenantDataSource = JndiHelper.lookupDataSource( tenantDataSourceName );        return tenantDataSource.getConnection();    }

    public void closeConnection(Connection conn) throws SQLException {        conn.close();    }

    public boolean supportsAggressiveRelease() {        // so long as the tenant identifier remains available in TenantContext throughout, we can        // reacquire later        return true;    }

    public void configure(Properties props) {        // currently nothing to do here    }

    public close() {        // currently nothing to do here    }}

public class MyTenantAwareConnectionProvider implements ConnectionProvider {    public static final String BASE_JNDI_NAME = "java:/comp/env/jdbc/";

    public Connection getConnection() throws SQLException {        final String tenantId = TenantContext.getTenantId()        final String tenantDataSourceName = BASE_JNDI_NAME + tenantId;        DataSource tenantDataSource = JndiHelper.lookupDataSource( tenantDataSourceName );        return tenantDataSource.getConnection();    }

    public void closeConnection(Connection conn) throws SQLException {        conn.close();    }

    public boolean supportsAggressiveRelease() {        // so long as the tenant identifier remains available in TenantContext throughout, we can        // reacquire later        return true;    }

    public void configure(Properties props) {        // currently nothing to do here    }

    public close() {        // currently nothing to do here    }}

Page 11: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Separate Schema ­ Usagepublic void demonstrateUsageInSeparateSchemaScenario() {    // The ConnectionProvider we saw earlier is registered with the SessionFactory as the means    // for Hibernate to acquire Connections as needed for the Session.  Here we must push the     // tenant­id to the TenantContext so it is available to the ConnectionProvider.  This is usually     // best handled by some form of “request interceptor” (HttpServletRequestFilter, etc) to make     // sure it is done uniformly

    TenantContext.setTenantId( “some­tenant­identifier” );

    // Creating an entity      Session session = openSession();    session.beginTransaction();    Customer customer = new Customer();    ...    session.persist( customer );    session.getTransaction().commit();    session.close();

    // Querying Customers      session = openSession();    session.beginTransaction();    Customer customer = (Customer) session.createQuery( “from Customer” ).uniqueResult();    session.getTransaction().commit();    session.close();}

public void demonstrateUsageInSeparateSchemaScenario() {    // The ConnectionProvider we saw earlier is registered with the SessionFactory as the means    // for Hibernate to acquire Connections as needed for the Session.  Here we must push the     // tenant­id to the TenantContext so it is available to the ConnectionProvider.  This is usually     // best handled by some form of “request interceptor” (HttpServletRequestFilter, etc) to make     // sure it is done uniformly

    TenantContext.setTenantId( “some­tenant­identifier” );

    // Creating an entity      Session session = openSession();    session.beginTransaction();    Customer customer = new Customer();    ...    session.persist( customer );    session.getTransaction().commit();    session.close();

    // Querying Customers      session = openSession();    session.beginTransaction();    Customer customer = (Customer) session.createQuery( “from Customer” ).uniqueResult();    session.getTransaction().commit();    session.close();}

Page 12: HibernateMultitenancy-2011-02-16

Multi-tenancy in Hibernate | Steve Ebersole

Support in Hibernate 4● Exact API still under discussion[1]● Public API options:

– Session.setTenantId(String tenantId)

– Passed as part of opening a Session

● Transparently handled by Hibernate

[1] http://opensource.atlassian.com/projects/hibernate/browse/HHH­5697