Я пытаюсь заставить работать решение для работы с несколькими арендаторами на основе схем, аналогично в этом примере , но с Oracle вместо Postgres.
Например, у меня есть три схемы: FOO,БАР и БАЗ.У каждого БАРА и БАЗА есть стол, который называется СООБЩЕНИЯ.FOO был предоставлен SELECT для BAR.MESSAGES и BAZ.MESSAGES.Поэтому, если я подключусь как FOO, а затем выполню
SELECT * FROM BAR.MESSAGES;
, я получу результат, как и ожидалось.Но если я опускаю имя схемы (например, SELECT * FROM MESSAGES), я получаю ORA-00942: таблица или представление не существует (соединение использует неверную схему).
Вот мой Dao / репозиторий:
@Repository
public interface MessageDao extends CrudRepository<Foo, Long> {
}
Контроллер:
@GetMapping("/findAll")
public List<Message> findAll() {
TenantContext.setCurrentTenant("BAR");
var result = messageDao.findAll();
return result;
}
Конфиг:
@Configuration
public class MessageConfig {
@Bean
public JpaVendorAdapter jpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
MultiTenantConnectionProvider multiTenantConnectionProvider,
CurrentTenantIdentifierResolver tenantIdentifierResolver) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan(Message.class.getPackageName());
em.setJpaVendorAdapter(this.jpaVendorAdapter());
Map<String, Object> jpaProperties = new HashMap<>();
jpaProperties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
jpaProperties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProvider);
jpaProperties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, tenantIdentifierResolver);
jpaProperties.put(Environment.FORMAT_SQL, true);
em.setJpaPropertyMap(jpaProperties);
return em;
}
МногопользовательскаяConnectionProvider:
@Component
public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider {
@Autowired
private DataSource dataSource;
@Override
public Connection getAnyConnection() throws SQLException {
return dataSource.getConnection();
}
@Override
public void releaseAnyConnection(Connection connection) throws SQLException {
connection.close();
}
@Override
public Connection getConnection(String currentTenantIdentifier) throws SQLException {
String tenantIdentifier = TenantContext.getCurrentTenant();
final Connection connection = getAnyConnection();
try (Statement statement = connection.createStatement()) {
statement.execute("ALTER SESSION SET CURRENT_SCHEMA = BAR");
} catch (SQLException e) {
throw new HibernateException("Problem setting schema to " + tenantIdentifier, e);
}
return connection;
}
@Override
public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException {
try (Statement statement = connection.createStatement()) {
statement.execute("ALTER SESSION SET CURRENT_SCHEMA = FOO");
} catch (SQLException e) {
throw new HibernateException("Problem setting schema to " + tenantIdentifier, e);
}
connection.close();
}
@SuppressWarnings("rawtypes")
@Override
public boolean isUnwrappableAs(Class unwrapType) {
return false;
}
@Override
public <T> T unwrap(Class<T> unwrapType) {
return null;
}
@Override
public boolean supportsAggressiveRelease() {
return true;
}
}
И TenantIdentifierResolver (хотя это и не очень актуально, потому что я жестко кодирую арендаторов прямо сейчас в ConnectionProviderImpl выше):
@Component
public class TenantIdentifierResolver implements CurrentTenantIdentifierResolver {
@Override
public String resolveCurrentTenantIdentifier() {
String tenantId = TenantContext.getCurrentTenant();
if (tenantId != null) {
return tenantId;
}
return "BAR";
}
@Override
public boolean validateExistingCurrentSessions() {
return true;
}
}
Любые идеи о том, почему базовое соединение неНе переключаете схемы, как ожидалось?
ОБНОВЛЕНИЕ 1
Возможно, это как-то связано с базовым соединением Oracle.В OracleConnection существует свойство с именем CONNECTION_PROPERTY_CREATE_DESCRIPTOR_USE_CURRENT_SCHEMA_FOR_SCHEMA_NAME .Документация гласит:
У пользователя также есть возможность добавить значение CURRENT_USER к имени ADT, чтобы получить полное имя, установив для этого свойства значение true.Обратите внимание, что для извлечения значения CURRENT_SCHEMA требуется обход в сеть.
Но проблема остается, даже если я установил это значение в true (-Doracle.jdbc.createDescriptorUseCurrentSchemaForSchemaName = true).Это может быть из-за того, что «имя пользователя» в Соединении по-прежнему «FOO», даже после изменения sesssion для установки схемы в «BAR» (currentSchema в Соединении - «BAR»).Но это будет означать, что документация OracleConnection неверна, не так ли?
ОБНОВЛЕНИЕ 2 Я также не учел тот факт, что здесь мы используем Spring Data JPA.Может быть, это как-то связано с проблемой?Я обнаружил, что если я включаю имя схемы, жестко закодированное в сущности, то оно работает (например, @Table (schema = "BAR")), но с жестко закодированным значением не является приемлемым решением.
Это может также сработать, если мы переписываем запросы как собственные @Query и затем включаем {h-schema} в SQL, но в Hibernate это схема по умолчанию, а не «текущая» (динамическая) схема, так что это не совсемтоже верно.