Настройка MultiTenantConnectionProvider с использованием Hibernate 4.2 и Spring 3.1.1
В настоящее время я пытаюсь настроить Hibernate для мульти-аренды, используя отдельный подход к Schema.
После работы над ним в течение 2 дней и просмотра почти всех источников, которые я смог найти через Google, я начинаю сильно разочаровываться.
В основном я пытаюсь следовать руководству, предоставленному в Hibernate Devguidehttp://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html_single/#d5e4691
Но, к сожалению, я не могу найти ConnectionProviderUtils для создания ConnectionProvider. В настоящее время я пытаюсь выяснить 2 пункта:
Почему метод configure (Properties props) моего MSSQLMultiTenantConnectionProvider никогда не вызывается. Исходя из того, что я интерпретировал из источника и описания различных других реализаций ConnectionProvider, я предполагаю, что этот метод будет вызываться для инициализации ConnectionProvider.
Так как я не могу работать с configure (Properties props), я попробовал другие подходы к получению свойств hibernate и DataSource, указанных в контексте приложения и 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;
}
}
Прямо сейчас есть 2 возможных подхода, которые я вижу, чтобы получить конфигурации, в которых я нуждаюсь из файлов конфигурации. Либо запустите метод 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>
Это вершинатрассировка стека исключений:
Вызывается: .beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods (AbstractAutowireCapableBeanFactory.java:1514) в org.springframework.beans.factory.support.
Я отследил эту проблему в режиме отладки и обнаружил, что проблема в том, что мой 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, К сожалению, я застрял именно в этом вопросе. ^^ "Но это проблема для другой темы. (Правка: выясняется, что это была только неправильная конфигурация из более раннего тестирования + одна старая зависимость)
Что касается этой темы, проблема остается в том, что я хочу каким-то образом иметь возможность повторно использовать DataSource, который у меня уже есть в конфигурации для использования Spring Security в любом случае, для Hibernate, чтобы избежать необходимости конфигурировать DataSource в двух местах. Таким образом, остается вопрос, как интегрировать использование источника данных в мой MultiTenantConnectionProvider. У кого-нибудь есть идеи о том, где можно найти какие-либо намеки на это?