В настоящее время я пытаюсь настроить Hibernate на несколько арендаторов, используя отдельный подход к схеме.
После работы над ним около 2 дней и просмотра почти каждого источника, который я могу найти через Google, я начинаю расстраиваться.
В основном я пытаюсь следовать руководству, представленному в Devguide Hibernate http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html_single/#d5e4691
Но, к сожалению, я не могу найти ConnectionProviderUtils для создания ConnectionProvider.
В настоящее время я пытаюсь выяснить 2 очка:
-
Почему метод configure (Свойства реквизита) моего MSSQLMultiTenantConnectionProvider никогда не вызывается. Из того, что я интерпретировал из источника и описания различных других реализаций ConnectionProvider, я предполагаю, что этот метод будет вызван для инициализации ConnectionProvider.
-
Так как я не могу работать с настройкой (реквизиты свойств), я опробовал другие подходы к получению свойств hibernate и DataSource, указанных в приложении Context и hibernate.cfg.xml. (Как впрыскивание источника данных непосредственно в ConnectionProvider)
Любые указатели на возможные способы решения этой проблемы (Методы, Классы, Учебники)
Итак, вот некоторые составляющие моей реализации:
Источник данных и 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 which sets the hibernate properties
DriverManagerConnectionProviderImpl defaultProvider = new DriverManagerConnectionProviderImpl();
defaultProvider.configure(properties);
Connection con = defaultProvider.getConnection();
ResultSet rs = con.createStatement().executeQuery("SELECT * FROM [schema].table");
rs.close(); //the statement and sql is just to test the connection
return defaultProvider.getConnection();
}
@Override
public Connection getConnection(String tenantIdentifier) throws SQLException {
<--not sure how to 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;
}
}
Прямо сейчас есть два возможных подхода, которые я вижу для получения конфигураций, которые мне нужны из конфигурационных файлов. Либо получите способ configure() для запуска, либо каким-то образом сделать инъекцию DataSource возможной. Я предполагаю, что первый из них был бы лучшим способом.
Важно отметить, что у меня был Hibernate и работает только для одного арендатора (это означает, что без использования MultiTenantConnectionProvider, используя стандартный ConnectionProvider, используемый Hibernate)
Уже большое спасибо всем, кто читает этот пост. С нетерпением ждем ответов.
С наилучшими пожеланиями
Обновление 1:
Я немного поиграл с этим и жестко закодировал ссылки на подключение в свой MultiTenantConnectionProvider (обновил код выше). Это отлично работает в отношении MultiTenantConnectionProvider. Но это все еще не решает моих проблем. Теперь мое приложение не удается инициализировать Менеджер транзакций:
<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>
Это верхняя часть stacktrace > :
Вызвано: 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)
Я проследил эту проблему в режиме отладки и выяснил, что проблема в том, что мой SessionFactory каким-то образом не овладевает DataSource. (Не имеет значения, укажу ли я DataSource в файле hibernate.cfg.xml или нет) Но при инициализации TransactionManager он пытается получить DataSource из SessionFactory и в результате не получается исключение NullPointerException. Есть ли у кого-нибудь намек на то, в какой точке внутренней работы спящего режима это терпит неудачу? Во всех документах и сообщениях, которые я видел, не было никаких указаний на то, что мне нужно обработать инъекцию DataSource в SessionFactory. На данный момент я просто думаю, что я пытаюсь выяснить, как получить DataSource в нужное место или как изменить поток инициализации. Если у кого-то есть лучшая идея, я был бы действительно счастлив.
Изменить: Также опубликовано это на форумах Hibernate:
Обновление 2:
Мне удалось обойти эту проблему, установив свойство autodetectDataSource в TransactionManager равным false:
<property name="autodetectDataSource" value="false"/>
Я получил этот намек со следующего сообщения http://forum.springsource.org/showthread.php?123478-SessionFactory-configured-for-multi-tenancy-but-no-tenant-identifier-specified. К сожалению, я сейчас придерживаюсь именно этой проблемы. ^^ "Но это проблема для другой темы. (Edit: Оказывается, это была неправильная конфигурация из более раннего тестирования + одна старая зависимость)
Что касается этого вопроса, остается проблема: я хочу как-то снова использовать DataSource, который у меня уже есть в конфигурации для использования Spring Security в любом случае, для Hibernate, чтобы избежать необходимости настраивать DataSource в двух местах. Поэтому остается вопрос, как интегрировать использование DataSource в моем MultiTenantConnectionProvider. Кто-нибудь имеет представление о том, где найти какие-либо намеки на это?