Setting up a MultiTenantConnectionProvider using Hibernate 4.2 and Spring 3.1.1 -


i trying set hibernate multi tenancy using seperate schema aproach.
after working on 2 days , browsing every source find via google starting quite frustrated.

basicaly trying follow guide provided in hibernate devguide http://docs.jboss.org/hibernate/orm/4.1/devguide/en-us/html_single/#d5e4691
unfortunately not able find connectionproviderutils build connectionprovider. trying figure out 2 points:

  1. why configure(properties props) method of mssqlmultitenantconnectionprovider never called. interpreted source of , description of different other connectionprovider implementions assuming method going called initialize connectionprovider.

  2. since not able work configure(properties props) tried out other approaches of somehow obtaining hibernate properties , datasource specified in application context , hibernate.cfg.xml. (like injecting datasource directly connectionprovider)

any pointers possible ways solve (methods, classes, tutorials)

so here relevant parts of implementation:
data source , hibernate.cfg.xml:

    <bean id="datasource"   class="org.springframework.jdbc.datasource.drivermanagerdatasource">         <property name="driverclassname" value="com.microsoft.sqlserver.jdbc.sqlserverdriver" />         <property name="url" value="jdbc:sqlserver://<host>:<port>;databasename=<dbname>;" />         <property name="username" value=<username> />         <property name="password" value=<password> />    </bean>    <bean id="sessionfactory" class="org.springframework.orm.hibernate4.localsessionfactorybean">         <!-- property name="datasource" ref="datasource" /-->         <property name="annotatedclasses">             <list>                 <value>c.h.utils.hibernate.user</value>                 <value>c.h.utils.hibernate.role</value>                 <value>c.h.utils.hibernate.tenant</value>             </list>         </property>         <property name="hibernateproperties">             <value>                 hibernate.dialect=org.hibernate.dialect.sqlserverdialect                 hibernate.show_sql=true                 hibernate.multitenancy=schema                 hibernate.tenant_identifier_resolver=c.h.utils.hibernate.currenttenantidentifierresolver                 hibernate.multi_tenant_connection_provider=c.h.utils.hibernate.mssqlmultitenantconnectionproviderimpl              </value>         </property>     </bean> 

mssqlmultitenantconnectionproviderimpl:

package c.hoell.utils.hibernate;  import java.sql.connection; import java.sql.sqlexception;  import javax.sql.datasource;  import org.hibernate.service.unknownunwraptypeexception; import org.hibernate.service.jdbc.connections.spi.connectionprovider; import org.hibernate.service.jdbc.connections.spi.multitenantconnectionprovider; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.service;  @service public class mssqlmultitenantconnectionproviderimpl implements multitenantconnectionprovider  {         private static final long serialversionuid = 8074002161278796379l;      @autowired     private datasource datasource;       public void configure(properties props) throws hibernateexception {      }       @override     public connection getanyconnection() throws sqlexception {         properties properties = getconnectionproperties(); //method sets hibernate properties          drivermanagerconnectionproviderimpl defaultprovider = new   drivermanagerconnectionproviderimpl();         defaultprovider.configure(properties);         connection con = defaultprovider.getconnection();         resultset rs = con.createstatement().executequery("select * [schema].table");         rs.close(); //the statement , sql test connection         return defaultprovider.getconnection();     }      @override     public connection getconnection(string tenantidentifier) throws sqlexception {         <--not sure how implement this-->     }      @override     public void releaseanyconnection(connection connection) throws sqlexception {         connection.close();      }      @override     public void releaseconnection(string tenantidentifier, connection connection){         try {             this.releaseanyconnection(connection);         } catch (sqlexception e) {             // todo auto-generated catch block             e.printstacktrace();         }     }      @override     public boolean supportsaggressiverelease() {         return false;     }      @override     public boolean isunwrappableas(class unwraptype) {         return connectionprovider.class.equals( unwraptype ) || multitenantconnectionprovider.class.equals( unwraptype ) || mssqlmultitenantconnectionproviderimpl.class.isassignablefrom( unwraptype );     }      @suppresswarnings("unchecked")     @override     public <t> t unwrap(class<t> unwraptype) {         if ( isunwrappableas( unwraptype ) ) {             return (t) this;         }         else {             throw new unknownunwraptypeexception( unwraptype );         }     }      public datasource getdatasource() {         return datasource;     }      public void setdatasource(datasource datasource) {         this.datasource = datasource;     }  } 

right there 2 possible approaches see obtaint configurations need config files. either configure() method run or somehow make injection of datasource possible. guess first 1 better way.

an important thing mention had hibernate , running 1 tenant (means without using multitenantconnectionprovider, using standard connectionprovider used hibernate)

already big reading post. looking forward answers.

best regards

update 1:

i have played around bit , hardcoded connectiondetails multitenantconnectionprovider (updated code above). working fine in regards multitenantconnectionprovider. still not solving problems. application fails @ initializing transaction manager:

<tx:annotation-driven transaction-manager="txmanager" proxy-target-class="true"/>     <bean id="txmanager" class="org.springframework.orm.hibernate4.hibernatetransactionmanager">         <property name="sessionfactory" ref="sessionfactory" />     </bean> 

this top of exception stacktrace:

caused by: java.lang.nullpointerexception @ org.springframework.orm.hibernate4.sessionfactoryutils.getdatasource(sessionfactoryutils.java:101) @ org.springframework.orm.hibernate4.hibernatetransactionmanager.afterpropertiesset(hibernatetransactionmanager.java:264) @ org.springframework.beans.factory.support.abstractautowirecapablebeanfactory.invokeinitmethods(abstractautowirecapablebeanfactory.java:1514) @ org.springframework.beans.factory.support.abstractautowirecapablebeanfactory.initializebean(abstractautowirecapablebeanfactory.java:1452)

i traced issue down in debug mode , found out problem sessionfactory somehow not getting hold of datasource. (it makes no difference whether specify datasource in hibernate.cfg.xml or not) when initializing transactionmanager tries datasource sessionfactory , fails nullpointerexception result. have hint @ point of inner workings of hibernate failing? in documentation , posts have seen there no indication need handle injection of datasource sessionfactory. guess try figure out how datasource needed place or how change initializing flow. if has better idea happy.

edit: posted in hibernate forums now:

update 2:

so managed around issue setting autodetectdatasource property in transactionmanager false:

<property name="autodetectdatasource" value="false"/> 

i got hint following post http://forum.springsource.org/showthread.php?123478-sessionfactory-configured-for-multi-tenancy-but-no-tenant-identifier-specified. unfortunately stuck @ issue. ^^" problem topic. (edit: turns out misconfiguration earlier testing + 1 old dependency)

as topic problem remains want somehow able reuse datasource, have in configuration use of spring security anyway, hibernate avoid need having configure datasource in 2 places. question still stands how integrate use of datasource in multitenantconnectionprovider. have idea on find hints on that?

according steve ebersole's comments on jira issue referred 1 of question's commenters (hhh-8752):

well first, not true hibernate "instantiates classes referred ... multi_tenant_connection_provider , multi_tenant_identifier_resolver". hibernate first tries treat these settings objects of intended types, (multitenantconnectionprovider multi_tenant_connection_provider , currenttenantidentifierresolver multi_tenant_identifier_resolver.

so pass beans in directly, configured want.

i followed suggestion , managed make work.

this currenttenantidentifierresolver defined spring bean:

@component @scope(value = "request", proxymode = scopedproxymode.target_class) public class requesturitenantidentifierresolver implements currenttenantidentifierresolver {      @autowired     private httpservletrequest request;      @override     public string resolvecurrenttenantidentifier() {         string[] pathelements = request.getrequesturi().split("/");         string tenant = pathelements[1];         return tenant;     }      @override     public boolean validateexistingcurrentsessions() {         return true;     } } 

this multitenantconnectionprovider defined spring bean:

@component public class schemapertenantconnectionproviderimpl implements multitenantconnectionprovider {      @autowired     private datasource datasource;      @override     public connection getanyconnection() throws sqlexception {         return datasource.getconnection();     }      @override     public void releaseanyconnection(final connection connection) throws sqlexception {         connection.close();     }      @override     public connection getconnection(final string tenantidentifier) throws sqlexception {         final connection connection = getanyconnection();         try {             connection.createstatement().execute("use " + tenantidentifier);         } catch (sqlexception e) {             throw new hibernateexception("could not alter jdbc connection specified schema [" + tenantidentifier + "]",                                          e);         }         return connection;     }      @override     public void releaseconnection(final string tenantidentifier, final connection connection) throws sqlexception {         try {             connection.createstatement().execute("use dummy");         } catch (sqlexception e) {             // on error, throw exception make sure connection not returned pool.             // requirements may differ             throw new hibernateexception(                     "could not alter jdbc connection specified schema [" +                             tenantidentifier + "]",                     e             );         }         connection.close();     }      @override     public boolean supportsaggressiverelease() {         return true;     }      @override     public boolean isunwrappableas(class aclass) {         return false;     }      @override     public <t> t unwrap(class<t> aclass) {         return null;     } } 

and finally, localcontainerentitymanagerfactorybean wired make use of 2 components above:

@configuration public class hibernateconfig {      @bean     public jpavendoradapter jpavendoradapter() {         return new hibernatejpavendoradapter();     }       @bean     public localcontainerentitymanagerfactorybean entitymanagerfactory(datasource datasource,                                                                        multitenantconnectionprovider multitenantconnectionprovider,                                                                        currenttenantidentifierresolver tenantidentifierresolver) {         localcontainerentitymanagerfactorybean emfbean = new localcontainerentitymanagerfactorybean();         emfbean.setdatasource(datasource);         emfbean.setpackagestoscan(vistojobsapplication.class.getpackage().getname());         emfbean.setjpavendoradapter(jpavendoradapter());          map<string, object> jpaproperties = new hashmap<>();         jpaproperties.put(org.hibernate.cfg.environment.multi_tenant,                           multitenancystrategy.schema);         jpaproperties.put(org.hibernate.cfg.environment.multi_tenant_connection_provider,                           multitenantconnectionprovider);         jpaproperties.put(org.hibernate.cfg.environment.multi_tenant_identifier_resolver,                           tenantidentifierresolver);         emfbean.setjpapropertymap(jpaproperties);         return emfbean;     } } 

the data source i'm using made available automatically spring boot.

i hope helps.


Popular posts from this blog

How to calculate SNR of signals in MATLAB? -

c# - Attempting to upload to FTP: System.Net.WebException: System error -

ios - UISlider customization: how to properly add shadow to custom knob image -