config: Exclude DataSourceAutoConfiguration and update tenant database connection URL in tenants.json.
This commit is contained in:
@@ -2,8 +2,9 @@ package com.magistr.app;
|
|||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
|
||||||
public class Application {
|
public class Application {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|||||||
@@ -5,16 +5,21 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||||
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManagerFactory;
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Конфигурация мультитенантного DataSource.
|
* Конфигурация мультитенантного DataSource.
|
||||||
@@ -38,6 +43,7 @@ public class TenantDataSourceConfig implements WebMvcConfigurer {
|
|||||||
private String defaultDbPassword;
|
private String defaultDbPassword;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@Primary
|
||||||
public TenantRoutingDataSource tenantRoutingDataSource() {
|
public TenantRoutingDataSource tenantRoutingDataSource() {
|
||||||
TenantRoutingDataSource routingDataSource = new TenantRoutingDataSource();
|
TenantRoutingDataSource routingDataSource = new TenantRoutingDataSource();
|
||||||
|
|
||||||
@@ -45,7 +51,7 @@ public class TenantDataSourceConfig implements WebMvcConfigurer {
|
|||||||
List<TenantConfig> tenants = loadTenantsFromFile();
|
List<TenantConfig> tenants = loadTenantsFromFile();
|
||||||
|
|
||||||
// Если нет тенантов и есть дефолтный datasource — создаём "default" тенант
|
// Если нет тенантов и есть дефолтный datasource — создаём "default" тенант
|
||||||
if (tenants.isEmpty() && !defaultDbUrl.isBlank()) {
|
if (tenants.isEmpty() && defaultDbUrl != null && !defaultDbUrl.isBlank()) {
|
||||||
TenantConfig defaultTenant = new TenantConfig(
|
TenantConfig defaultTenant = new TenantConfig(
|
||||||
"Default", "default", defaultDbUrl, defaultDbUsername, defaultDbPassword
|
"Default", "default", defaultDbUrl, defaultDbUsername, defaultDbPassword
|
||||||
);
|
);
|
||||||
@@ -62,18 +68,44 @@ public class TenantDataSourceConfig implements WebMvcConfigurer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tenants.isEmpty()) {
|
if (routingDataSource.getTenantConfigs().isEmpty()) {
|
||||||
log.warn("No tenants configured! Backend will fail on DB queries.");
|
log.warn("=== НЕТ НАСТРОЕННЫХ ТЕНАНТОВ ===");
|
||||||
|
log.warn("Backend запустится, но API не будет работать без подключения к БД.");
|
||||||
|
log.warn("Добавьте тенант через POST /api/database/tenants или настройте tenants.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
return routingDataSource;
|
return routingDataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@Primary
|
||||||
public DataSource dataSource(TenantRoutingDataSource tenantRoutingDataSource) {
|
public DataSource dataSource(TenantRoutingDataSource tenantRoutingDataSource) {
|
||||||
return tenantRoutingDataSource;
|
return tenantRoutingDataSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public LocalContainerEntityManagerFactoryBean entityManagerFactory(
|
||||||
|
DataSource dataSource, EntityManagerFactoryBuilder builder) {
|
||||||
|
|
||||||
|
Map<String, String> jpaProps = new HashMap<>();
|
||||||
|
jpaProps.put("hibernate.hbm2ddl.auto", "update");
|
||||||
|
jpaProps.put("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
|
||||||
|
|
||||||
|
return builder
|
||||||
|
.dataSource(dataSource)
|
||||||
|
.packages("com.magistr.app.model")
|
||||||
|
.persistenceUnit("default")
|
||||||
|
.properties(jpaProps)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@Primary
|
||||||
|
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
|
||||||
|
return new JpaTransactionManager(emf);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public TenantInterceptor tenantInterceptor() {
|
public TenantInterceptor tenantInterceptor() {
|
||||||
return new TenantInterceptor();
|
return new TenantInterceptor();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
|
|||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
@@ -21,6 +22,13 @@ public class TenantRoutingDataSource extends AbstractRoutingDataSource {
|
|||||||
|
|
||||||
private final Map<String, TenantConfig> tenantConfigs = new ConcurrentHashMap<>();
|
private final Map<String, TenantConfig> tenantConfigs = new ConcurrentHashMap<>();
|
||||||
private final Map<Object, Object> dataSources = new ConcurrentHashMap<>();
|
private final Map<Object, Object> dataSources = new ConcurrentHashMap<>();
|
||||||
|
private boolean initialized = false;
|
||||||
|
|
||||||
|
public TenantRoutingDataSource() {
|
||||||
|
// Устанавливаем пустой map чтобы afterPropertiesSet не падал
|
||||||
|
setTargetDataSources(new HashMap<>());
|
||||||
|
setLenientFallback(false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object determineCurrentLookupKey() {
|
protected Object determineCurrentLookupKey() {
|
||||||
@@ -41,6 +49,7 @@ public class TenantRoutingDataSource extends AbstractRoutingDataSource {
|
|||||||
// Обновляем target data sources
|
// Обновляем target data sources
|
||||||
setTargetDataSources(dataSources);
|
setTargetDataSources(dataSources);
|
||||||
afterPropertiesSet();
|
afterPropertiesSet();
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
log.info("Added tenant '{}' -> {}", domain, config.getUrl());
|
log.info("Added tenant '{}' -> {}", domain, config.getUrl());
|
||||||
}
|
}
|
||||||
@@ -108,6 +117,10 @@ public class TenantRoutingDataSource extends AbstractRoutingDataSource {
|
|||||||
return tenantConfigs.containsKey(domain.toLowerCase());
|
return tenantConfigs.containsKey(domain.toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isInitialized() {
|
||||||
|
return initialized && !dataSources.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
private HikariDataSource createDataSource(TenantConfig config) {
|
private HikariDataSource createDataSource(TenantConfig config) {
|
||||||
HikariDataSource ds = new HikariDataSource();
|
HikariDataSource ds = new HikariDataSource();
|
||||||
ds.setJdbcUrl(config.getUrl());
|
ds.setJdbcUrl(config.getUrl());
|
||||||
@@ -119,6 +132,8 @@ public class TenantRoutingDataSource extends AbstractRoutingDataSource {
|
|||||||
ds.setConnectionTimeout(10000);
|
ds.setConnectionTimeout(10000);
|
||||||
ds.setIdleTimeout(300000);
|
ds.setIdleTimeout(300000);
|
||||||
ds.setMaxLifetime(600000);
|
ds.setMaxLifetime(600000);
|
||||||
|
// Не падать при инициализации если БД недоступна
|
||||||
|
ds.setInitializationFailTimeout(-1);
|
||||||
return ds;
|
return ds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
{
|
{
|
||||||
"name": "Default (dev)",
|
"name": "Default (dev)",
|
||||||
"domain": "default",
|
"domain": "default",
|
||||||
"url": "jdbc:postgresql://db:5432/app_db",
|
"url": "jdbc:postgresql://192.168.1.87:5432/app_db",
|
||||||
"username": "myuser",
|
"username": "myuser",
|
||||||
"password": "supersecretpassword"
|
"password": "supersecretpassword"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user