config: Exclude DataSourceAutoConfiguration and update tenant database connection URL in tenants.json.
Some checks failed
Build and Push Docker Images / build-and-push-backend (push) Successful in 29s
Build and Push Docker Images / build-and-push-frontend (push) Successful in 11s
Build and Push Docker Images / deploy-to-k8s (push) Has been cancelled

This commit is contained in:
Zuev
2026-03-12 22:31:09 +03:00
parent 14cc006f06
commit 3579ef9f1c
4 changed files with 56 additions and 8 deletions

View File

@@ -2,8 +2,9 @@ package com.magistr.app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@SpringBootApplication
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {

View File

@@ -5,16 +5,21 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.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.WebMvcConfigurer;
import jakarta.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
/**
* Конфигурация мультитенантного DataSource.
@@ -38,6 +43,7 @@ public class TenantDataSourceConfig implements WebMvcConfigurer {
private String defaultDbPassword;
@Bean
@Primary
public TenantRoutingDataSource tenantRoutingDataSource() {
TenantRoutingDataSource routingDataSource = new TenantRoutingDataSource();
@@ -45,7 +51,7 @@ public class TenantDataSourceConfig implements WebMvcConfigurer {
List<TenantConfig> tenants = loadTenantsFromFile();
// Если нет тенантов и есть дефолтный datasource — создаём "default" тенант
if (tenants.isEmpty() && !defaultDbUrl.isBlank()) {
if (tenants.isEmpty() && defaultDbUrl != null && !defaultDbUrl.isBlank()) {
TenantConfig defaultTenant = new TenantConfig(
"Default", "default", defaultDbUrl, defaultDbUsername, defaultDbPassword
);
@@ -62,18 +68,44 @@ public class TenantDataSourceConfig implements WebMvcConfigurer {
}
}
if (tenants.isEmpty()) {
log.warn("No tenants configured! Backend will fail on DB queries.");
if (routingDataSource.getTenantConfigs().isEmpty()) {
log.warn("=== НЕТ НАСТРОЕННЫХ ТЕНАНТОВ ===");
log.warn("Backend запустится, но API не будет работать без подключения к БД.");
log.warn("Добавьте тенант через POST /api/database/tenants или настройте tenants.json");
}
return routingDataSource;
}
@Bean
@Primary
public DataSource dataSource(TenantRoutingDataSource 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
public TenantInterceptor tenantInterceptor() {
return new TenantInterceptor();

View File

@@ -8,6 +8,7 @@ import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
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<Object, Object> dataSources = new ConcurrentHashMap<>();
private boolean initialized = false;
public TenantRoutingDataSource() {
// Устанавливаем пустой map чтобы afterPropertiesSet не падал
setTargetDataSources(new HashMap<>());
setLenientFallback(false);
}
@Override
protected Object determineCurrentLookupKey() {
@@ -41,6 +49,7 @@ public class TenantRoutingDataSource extends AbstractRoutingDataSource {
// Обновляем target data sources
setTargetDataSources(dataSources);
afterPropertiesSet();
initialized = true;
log.info("Added tenant '{}' -> {}", domain, config.getUrl());
}
@@ -108,6 +117,10 @@ public class TenantRoutingDataSource extends AbstractRoutingDataSource {
return tenantConfigs.containsKey(domain.toLowerCase());
}
public boolean isInitialized() {
return initialized && !dataSources.isEmpty();
}
private HikariDataSource createDataSource(TenantConfig config) {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl(config.getUrl());
@@ -119,6 +132,8 @@ public class TenantRoutingDataSource extends AbstractRoutingDataSource {
ds.setConnectionTimeout(10000);
ds.setIdleTimeout(300000);
ds.setMaxLifetime(600000);
// Не падать при инициализации если БД недоступна
ds.setInitializationFailTimeout(-1);
return ds;
}
}

View File

@@ -2,7 +2,7 @@
{
"name": "Default (dev)",
"domain": "default",
"url": "jdbc:postgresql://db:5432/app_db",
"url": "jdbc:postgresql://192.168.1.87:5432/app_db",
"username": "myuser",
"password": "supersecretpassword"
}