2014-12-28 73 views
1

覺得我很密切的多租戶執行的問題,如果這個工程應該幫助了很多人:)
是繼所以這是非常有用的鏈接:Manage Connection Pooling in multi-tenant web app with Spring, Hibernate and C3P0
我的問題是,在我的類,它實現 - MultiTenantConnectionProvider休眠模式,injectservices

我沒有得到一個句柄javax.sql.Datasource取得
這裏是我的全碼:
彈簧應用程序上下文:

<bean id="testService" class="com.mkyong.common.service.TestServiceImpl" lazy-init="true"> 
    <property name="testDao" ref="testDao" /> 
</bean> 

<bean id="testDao" class="com.mkyong.common.dao.TestDaoImpl" lazy-init="true"> 
    <!-- Injecting Standard Session Factory --> 
    <property name="sessionFactory" ref="sessionFactoryWorking" /> 
</bean> 

<!-- this seems to work --> 
<jee:jndi-lookup id="dataSource" jndi-name="MYSQLDS"/> 

<!-- SessionFactories --> 
<!-- Standard Session Factory --> 
<bean id="sessionFactoryWorking" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:local.JADE.PIT.hibernate.cfg.xml" /> 
    <property name="dataSource" ref="dataSource" /> 
</bean> 

<bean id="transactionManagerWorking" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> 
    <property name="autodetectDataSource" value="false" /> 
    <property name="sessionFactory" ref="sessionFactoryWorking" /> 
</bean> 

在文件local.JADE.PIT.hibernate.cfg.xml

<hibernate-configuration> 
<session-factory> 
    <property name="show_sql">true</property> 
    <property name="multiTenancy">SCHEMA</property> 
    <property name="multi_tenant_connection_provider">com.mkyong.common.provider.MySQLMultiTenantConnectionProviderImpl</property> 
    &globalpit; 
</session-factory> 

這裏是我的MySQLMultiTenantConnectionProviderImpl

package com.mkyong.common.provider; 

import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; 
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; 
import org.hibernate.service.UnknownUnwrapTypeException; 
import org.hibernate.service.spi.ServiceRegistryAwareService; 
import org.hibernate.service.spi.ServiceRegistryImplementor; 
import org.hibernate.engine.config.spi.ConfigurationService; 
import org.hibernate.cfg.Environment; 

import java.util.Map; 
import java.sql.Connection; 
import java.sql.SQLException; 
import javax.sql.DataSource; 

public class MySQLMultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider,ServiceRegistryAwareService{ 

private DataSource lazyDatasource;; 

@Override 
public void injectServices(ServiceRegistryImplementor serviceRegistry) { 
    Map lSettings = serviceRegistry.getService(ConfigurationService.class).getSettings(); 
    System.out.println(" satish ********************** " + Environment.DATASOURCE); 
    System.out.println(" satish ********************** " + lSettings.get(Environment.DATASOURCE)); 
    lazyDatasource = (DataSource) lSettings.get(Environment.DATASOURCE); 
} 

@Override 
public boolean supportsAggressiveRelease() { 
    System.out.println("<<<<<<< satish supportsAggressiveRelease >>>>>>>>>"); 
    /** this method must be overriden **/ 
    return false; 
} 

@Override 
public void releaseConnection(String tenantIdentifier, Connection connection){ 
    /** this method must be overriden **/ 
    System.out.println("<<<<<<< satish releaseConnection 1 >>>>>>>>>"); 
    try { 
     this.releaseAnyConnection(connection); 
    }catch (SQLException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 

@Override 
public void releaseAnyConnection(Connection connection) throws SQLException { 
    System.out.println("<<<<<<< satish releaseAnyConnection 2 >>>>>>>>>"); 
    /** this method must be overriden **/ 
    connection.close(); 
} 

@Override 
public Connection getConnection(String tenantIdentifier) throws SQLException { 
    System.out.println("<<<<<<< satish getConnection 1 >>>>>>>>>"); 
    final Connection connection = getAnyConnection(); 
    System.out.println("<<<<<<< satish getConnection 2 >>>>>>>>>"); 
    try { 
     /** this is the place where we can change our schema based on identifier **/ 
     connection.createStatement().execute("USE " + tenantIdentifier); 
    }catch (SQLException e) { 
     e.printStackTrace(); 
     throw new HibernateException("Could not alter JDBC connection to specified schema [" + tenantIdentifier + "]", e); 
    } 
    return connection; 
} 

@Override 
public Connection getAnyConnection() throws SQLException { 
/** this method is getting called first **/ 
System.out.println("<<<<<<< satish getAnyConnection >>>>>>>>>"); 
return lazyDatasource.getConnection(); 
} 

@SuppressWarnings("unchecked") 
@Override 
public <T> T unwrap(Class<T> unwrapType) { 
    if (isUnwrappableAs(unwrapType)) { 
     return (T) this; 
    }else { 
     throw new UnknownUnwrapTypeException(unwrapType); 
    } 

}

@Override 
public boolean isUnwrappableAs(Class unwrapType) { 
return ConnectionProvider.class.equals(unwrapType) || MultiTenantConnectionProvider.class.equals(unwrapType) || MySQLMultiTenantConnectionProviderImpl.class.isAssignableFrom(unwrapType); 
} 
} 

這裏是我的DAO:

public class TestDaoImpl{ 

/** schema choice which comes from UI/http - which is outside the scope of this example **/ 
private String schema = null; 

/** this is the injected way which works **/ 
private SessionFactory sessionFactory; 

public final void setSessionFactory(SessionFactory sessionFactory) { 
    this.sessionFactory = sessionFactory; 
} 

public void setSchema(String schema) { 
      this.schema = schema; 
} 

public Session getCurrentSession() { 
    Session session = null; 
    try { 
     **/** this is where we are getting a connection based on client/tenant **/** 
     session = getSessionFactory().withOptions().tenantIdentifier(schema).openSession(); 
    } catch (HibernateException e) { 
     e.printStackTrace(); 
     System.out.println("<<<<<< inside exception while getting session from sf >>>>>"); 
     session = getSessionFactory().openSession(); 
    } 

    return session; 
} 
@SuppressWarnings("unchecked") 
public List<Person> list() { 
    Session session = getCurrentSession(); 
    List<Person> personList = session.createQuery("from Person").list(); 
    session.close(); 
    return personList; 
} 

}

所以發生的是,該方法 - injectServices - 它是越來越援引
然而,數據源是空在下面一行:
lSettings .get(Environment.DATASOURCE)

如果我在我的cfg.xml文件中設置數據源名稱 -
MYSQLDS

然後我得到的返回值「MYSQLDS」 - 但它是一個字符串 - 所以在試圖投和javax.sql.DataSource

多幾個音符就那麼失敗:
通過文字讀取休眠文件詞,因爲這是非常非常重要的 - 他們是在給窮人的例子 - 但文檔需要一個完全新的和增強的意義 - 如果你仔細閱讀:) - http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html/ch16.html

注 - 我的數據源是一個weblogic jndi提供的datasour CE
感謝

編輯1 2014年12月29日11:35 AM IST
如前所述由M Deinum - 問題是,我沒有有線我的數據源
現在確實是工作 - 上述更新的代碼 - 這樣這應該工作,並幫助他人呢!
重申發佈的步驟的執行順序:
在我們的UI層,我們都知道它的客戶端在訪問應用
我們通過這個客戶端/租戶信息到DAO層
的DAO被注入SessionFactory的是在Spring
配置在DAO - 我們基於租戶獲取會話 - 的getCurrentSession()
這是非常重要的是得到基於租戶conenction
會話=了getSessionFactory()withOptions()tenantIdentifier(模式).openSession(。 );

現在,一旦我們有了會話,我們撥打電話:

List<Person> personList = session.createQuery("from Person").list(); 

此時類MySQLMultiTenantConnectionProviderImpl被調用

public Connection getConnection(String tenantIdentifier) throws SQLException { 

這的下面方法的地方, mojo發生/我們預計寫 - 你改變架構
在我的情況我使用的MySQL,所以語法會根據t他數據庫中正在使用:

connection.createStatement().execute("USE " + tenantIdentifier); 

完蛋了 - 多租戶(獨立的模式)的這種做法現在工作
還要說明一點 - 我沒有使用 - CurrentTenantIdentifierResolver
Hibernate文檔:(我不明白第一下面的段落 - 但是第二段似乎表明,這個類是沒有必要的,如果指定從SessionFactory的承包者標識符) - 這是我在做什麼,所以我沒有定義類 - CurrentTenantIdentifierResolver

有2種情形w ^這裏使用CurrentTenantIdentifierResolver:

The first situation is when the application is using the 
org.hibernate.context.spi.CurrentSessionContext feature in conjunction with multi-tenancy. 
In the case of the current-session feature, Hibernate will need to open a session if it cannot 
find an existing one in scope. However, when a session is opened in a multi-tenant environment 
the tenant identifier has to be specified. This is where the CurrentTenantIdentifierResolver 
comes into play; Hibernate will consult the implementation you provide to determine the tenant 
identifier to use when opening the session. In this case, it is required that a 
CurrentTenantIdentifierResolver be supplied. 

The other situation is when you do not want to have to explicitly specify the tenant identifier 
all the time as we saw in Example 16.1, 「Specifying tenant identifier from SessionFactory」. 
If a CurrentTenantIdentifierResolver has been specified, Hibernate will use it to determine 
the default tenant identifier to use when opening the session. 

再次非常感謝到M Deinum爲輕推我在這個方向及幫助
感謝

+0

對於初學者來說,你的數據源不會被注入到會話工廠,它只會被查找,在忘記之後,你應該在配置中添加''。 –

+0

@ m-deinum - 感謝您投入 - 我實際上是要爲您寫評論 - 是的 - 您的建議似乎有效 - 我會做更多的測試並回寫所做的任何更改 - 非常感謝您的幫助:) - 你可以發佈你的答案,以便它可以被接受 - 不能說我多麼感激! –

回答

2
<jee:jndi-lookup id="dataSource" jndi-name="MYSQLDS"/> 

<bean id="sessionFactoryWorking" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:local.JADE.PIT.hibernate.cfg.xml" /> 
</bean> 

DataSource上面的配置擡頭這就是它。它不被用於它只是坐在附近。您想通過將其注入dataSource屬性將其連接到您的LocalSessionFactoryBean

<bean id="sessionFactoryWorking" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:local.JADE.PIT.hibernate.cfg.xml" /> 
    <property name="dataSource" ref="dataSource" /> 
</bean> 

使用此配置,數據源已連線且可用。

由於注入數據源,您的hibernate配置文件中的另一件事connection.*屬性是無用的。

+0

@ m-deinum - 謝謝 - 已經測試了兩種模式,它正在工作! - 已經從cfg文件中刪除了**連接。* ** - 也進行了更新/修改,並將其理解爲上面的代碼行爲 –