diff --git a/backend/src/main/java/de/effigenix/EffigenixApplication.java b/backend/src/main/java/de/effigenix/EffigenixApplication.java index 2d87769..f17d281 100644 --- a/backend/src/main/java/de/effigenix/EffigenixApplication.java +++ b/backend/src/main/java/de/effigenix/EffigenixApplication.java @@ -1,8 +1,8 @@ package de.effigenix; +import de.effigenix.infrastructure.config.DatabaseProfileInitializer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; import org.springframework.scheduling.annotation.EnableAsync; /** @@ -15,11 +15,12 @@ import org.springframework.scheduling.annotation.EnableAsync; * - Shared Kernel: Cross-cutting concerns (AuthorizationPort, Result, etc.) */ @SpringBootApplication -@EnableJpaAuditing @EnableAsync public class EffigenixApplication { public static void main(String[] args) { - SpringApplication.run(EffigenixApplication.class, args); + var app = new SpringApplication(EffigenixApplication.class); + app.addInitializers(new DatabaseProfileInitializer()); + app.run(args); } } diff --git a/backend/src/main/java/de/effigenix/infrastructure/audit/DatabaseAuditLogger.java b/backend/src/main/java/de/effigenix/infrastructure/audit/DatabaseAuditLogger.java index ea3d50e..bc74709 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/audit/DatabaseAuditLogger.java +++ b/backend/src/main/java/de/effigenix/infrastructure/audit/DatabaseAuditLogger.java @@ -6,6 +6,7 @@ import de.effigenix.shared.security.ActorId; import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -30,6 +31,7 @@ import java.util.UUID; * - Uses REQUIRES_NEW transaction to ensure audit logs are committed even if business transaction fails */ @Service +@Profile("!no-db") public class DatabaseAuditLogger implements AuditLogger { private static final Logger log = LoggerFactory.getLogger(DatabaseAuditLogger.class); diff --git a/backend/src/main/java/de/effigenix/infrastructure/config/DatabaseProfileInitializer.java b/backend/src/main/java/de/effigenix/infrastructure/config/DatabaseProfileInitializer.java new file mode 100644 index 0000000..1088ed5 --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/config/DatabaseProfileInitializer.java @@ -0,0 +1,34 @@ +package de.effigenix.infrastructure.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.ConfigurableEnvironment; + +import java.sql.DriverManager; + +/** + * Prüft vor dem Context-Start, ob die Datenbank erreichbar ist. + * Schlägt die Verbindung fehl, wird das Spring-Profil "no-db" aktiviert. + * Damit werden JPA, DataSource und Liquibase ausgeschlossen und Stub-Beans registriert. + */ +public class DatabaseProfileInitializer implements ApplicationContextInitializer { + + private static final Logger log = LoggerFactory.getLogger(DatabaseProfileInitializer.class); + + @Override + public void initialize(ConfigurableApplicationContext ctx) { + var env = ctx.getEnvironment(); + var url = env.getProperty("spring.datasource.url", "jdbc:postgresql://localhost:5432/effigenix"); + var user = env.getProperty("spring.datasource.username", "effigenix"); + var pass = env.getProperty("spring.datasource.password", "effigenix"); + + try (var ignored = DriverManager.getConnection(url, user, pass)) { + log.debug("Datenbankverbindung erfolgreich – normaler Start."); + } catch (Exception e) { + log.warn("⚠️ Keine Datenbankverbindung – Stub-Modus aktiv. Einige Features sind nicht verfügbar. ({})", e.getMessage()); + ((ConfigurableEnvironment) env).addActiveProfile("no-db"); + } + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/config/JpaAuditingConfig.java b/backend/src/main/java/de/effigenix/infrastructure/config/JpaAuditingConfig.java new file mode 100644 index 0000000..ff165e2 --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/config/JpaAuditingConfig.java @@ -0,0 +1,14 @@ +package de.effigenix.infrastructure.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +/** + * Aktiviert JPA-Auditing nur, wenn eine Datenbankverbindung vorhanden ist. + */ +@Configuration +@Profile("!no-db") +@EnableJpaAuditing +public class JpaAuditingConfig { +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaArticleRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaArticleRepository.java index a76f142..7da7dd7 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaArticleRepository.java +++ b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaArticleRepository.java @@ -4,6 +4,7 @@ import de.effigenix.domain.masterdata.*; import de.effigenix.infrastructure.masterdata.persistence.mapper.ArticleMapper; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -12,6 +13,7 @@ import java.util.Optional; import java.util.stream.Collectors; @Repository +@Profile("!no-db") @Transactional(readOnly = true) public class JpaArticleRepository implements ArticleRepository { diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaCustomerRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaCustomerRepository.java index 00d822b..311b87a 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaCustomerRepository.java +++ b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaCustomerRepository.java @@ -5,6 +5,7 @@ import de.effigenix.infrastructure.masterdata.persistence.entity.FrameContractEn import de.effigenix.infrastructure.masterdata.persistence.mapper.CustomerMapper; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -13,6 +14,7 @@ import java.util.Optional; import java.util.stream.Collectors; @Repository +@Profile("!no-db") @Transactional(readOnly = true) public class JpaCustomerRepository implements CustomerRepository { diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaProductCategoryRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaProductCategoryRepository.java index a9b71a7..8828fc6 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaProductCategoryRepository.java +++ b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaProductCategoryRepository.java @@ -7,6 +7,7 @@ import de.effigenix.domain.masterdata.ProductCategoryRepository; import de.effigenix.infrastructure.masterdata.persistence.mapper.ProductCategoryMapper; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -15,6 +16,7 @@ import java.util.Optional; import java.util.stream.Collectors; @Repository +@Profile("!no-db") @Transactional(readOnly = true) public class JpaProductCategoryRepository implements ProductCategoryRepository { diff --git a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaSupplierRepository.java b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaSupplierRepository.java index 4af2358..92efd78 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaSupplierRepository.java +++ b/backend/src/main/java/de/effigenix/infrastructure/masterdata/persistence/repository/JpaSupplierRepository.java @@ -4,6 +4,7 @@ import de.effigenix.domain.masterdata.*; import de.effigenix.infrastructure.masterdata.persistence.mapper.SupplierMapper; import de.effigenix.shared.common.RepositoryError; import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -12,6 +13,7 @@ import java.util.Optional; import java.util.stream.Collectors; @Repository +@Profile("!no-db") @Transactional(readOnly = true) public class JpaSupplierRepository implements SupplierRepository { diff --git a/backend/src/main/java/de/effigenix/infrastructure/stub/NoOpAuditLogger.java b/backend/src/main/java/de/effigenix/infrastructure/stub/NoOpAuditLogger.java new file mode 100644 index 0000000..9db18af --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/stub/NoOpAuditLogger.java @@ -0,0 +1,36 @@ +package de.effigenix.infrastructure.stub; + +import de.effigenix.application.usermanagement.AuditEvent; +import de.effigenix.application.usermanagement.AuditLogger; +import de.effigenix.shared.security.ActorId; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Service; + +/** + * No-Op-Implementierung von AuditLogger für den Stub-Modus (keine Datenbankverbindung). + * Audit-Events werden verworfen. + */ +@Service +@Profile("no-db") +public class NoOpAuditLogger implements AuditLogger { + + @Override + public void log(AuditEvent event, String entityId, ActorId performedBy) { + // Stub: kein Audit-Log + } + + @Override + public void log(AuditEvent event, String details) { + // Stub: kein Audit-Log + } + + @Override + public void log(AuditEvent event, String entityId, String details, ActorId performedBy) { + // Stub: kein Audit-Log + } + + @Override + public void log(AuditEvent event, ActorId performedBy) { + // Stub: kein Audit-Log + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/stub/StubArticleRepository.java b/backend/src/main/java/de/effigenix/infrastructure/stub/StubArticleRepository.java new file mode 100644 index 0000000..03f5a83 --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/stub/StubArticleRepository.java @@ -0,0 +1,58 @@ +package de.effigenix.infrastructure.stub; + +import de.effigenix.domain.masterdata.Article; +import de.effigenix.domain.masterdata.ArticleId; +import de.effigenix.domain.masterdata.ArticleNumber; +import de.effigenix.domain.masterdata.ArticleRepository; +import de.effigenix.domain.masterdata.ArticleStatus; +import de.effigenix.domain.masterdata.ProductCategoryId; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@Profile("no-db") +public class StubArticleRepository implements ArticleRepository { + + private static final RepositoryError.DatabaseError STUB_ERROR = + new RepositoryError.DatabaseError("Stub-Modus: keine Datenbankverbindung"); + + @Override + public Result> findById(ArticleId id) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findAll() { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByCategory(ProductCategoryId categoryId) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByStatus(ArticleStatus status) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result save(Article article) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result delete(Article article) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result existsByArticleNumber(ArticleNumber articleNumber) { + return Result.failure(STUB_ERROR); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/stub/StubCustomerRepository.java b/backend/src/main/java/de/effigenix/infrastructure/stub/StubCustomerRepository.java new file mode 100644 index 0000000..31a14af --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/stub/StubCustomerRepository.java @@ -0,0 +1,58 @@ +package de.effigenix.infrastructure.stub; + +import de.effigenix.domain.masterdata.Customer; +import de.effigenix.domain.masterdata.CustomerId; +import de.effigenix.domain.masterdata.CustomerName; +import de.effigenix.domain.masterdata.CustomerRepository; +import de.effigenix.domain.masterdata.CustomerStatus; +import de.effigenix.domain.masterdata.CustomerType; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@Profile("no-db") +public class StubCustomerRepository implements CustomerRepository { + + private static final RepositoryError.DatabaseError STUB_ERROR = + new RepositoryError.DatabaseError("Stub-Modus: keine Datenbankverbindung"); + + @Override + public Result> findById(CustomerId id) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findAll() { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByType(CustomerType type) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByStatus(CustomerStatus status) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result save(Customer customer) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result delete(Customer customer) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result existsByName(CustomerName name) { + return Result.failure(STUB_ERROR); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/stub/StubProductCategoryRepository.java b/backend/src/main/java/de/effigenix/infrastructure/stub/StubProductCategoryRepository.java new file mode 100644 index 0000000..04d482f --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/stub/StubProductCategoryRepository.java @@ -0,0 +1,46 @@ +package de.effigenix.infrastructure.stub; + +import de.effigenix.domain.masterdata.CategoryName; +import de.effigenix.domain.masterdata.ProductCategory; +import de.effigenix.domain.masterdata.ProductCategoryId; +import de.effigenix.domain.masterdata.ProductCategoryRepository; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@Profile("no-db") +public class StubProductCategoryRepository implements ProductCategoryRepository { + + private static final RepositoryError.DatabaseError STUB_ERROR = + new RepositoryError.DatabaseError("Stub-Modus: keine Datenbankverbindung"); + + @Override + public Result> findById(ProductCategoryId id) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findAll() { + return Result.failure(STUB_ERROR); + } + + @Override + public Result save(ProductCategory category) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result delete(ProductCategory category) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result existsByName(CategoryName name) { + return Result.failure(STUB_ERROR); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/stub/StubRoleRepository.java b/backend/src/main/java/de/effigenix/infrastructure/stub/StubRoleRepository.java new file mode 100644 index 0000000..22b02e6 --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/stub/StubRoleRepository.java @@ -0,0 +1,51 @@ +package de.effigenix.infrastructure.stub; + +import de.effigenix.domain.usermanagement.Role; +import de.effigenix.domain.usermanagement.RoleId; +import de.effigenix.domain.usermanagement.RoleName; +import de.effigenix.domain.usermanagement.RoleRepository; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@Profile("no-db") +public class StubRoleRepository implements RoleRepository { + + private static final RepositoryError.DatabaseError STUB_ERROR = + new RepositoryError.DatabaseError("Stub-Modus: keine Datenbankverbindung"); + + @Override + public Result> findById(RoleId id) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByName(RoleName name) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findAll() { + return Result.failure(STUB_ERROR); + } + + @Override + public Result save(Role role) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result delete(Role role) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result existsByName(RoleName name) { + return Result.failure(STUB_ERROR); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/stub/StubSupplierRepository.java b/backend/src/main/java/de/effigenix/infrastructure/stub/StubSupplierRepository.java new file mode 100644 index 0000000..ff99609 --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/stub/StubSupplierRepository.java @@ -0,0 +1,52 @@ +package de.effigenix.infrastructure.stub; + +import de.effigenix.domain.masterdata.Supplier; +import de.effigenix.domain.masterdata.SupplierId; +import de.effigenix.domain.masterdata.SupplierName; +import de.effigenix.domain.masterdata.SupplierRepository; +import de.effigenix.domain.masterdata.SupplierStatus; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@Profile("no-db") +public class StubSupplierRepository implements SupplierRepository { + + private static final RepositoryError.DatabaseError STUB_ERROR = + new RepositoryError.DatabaseError("Stub-Modus: keine Datenbankverbindung"); + + @Override + public Result> findById(SupplierId id) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findAll() { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByStatus(SupplierStatus status) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result save(Supplier supplier) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result delete(Supplier supplier) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result existsByName(SupplierName name) { + return Result.failure(STUB_ERROR); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/stub/StubUserRepository.java b/backend/src/main/java/de/effigenix/infrastructure/stub/StubUserRepository.java new file mode 100644 index 0000000..72485d0 --- /dev/null +++ b/backend/src/main/java/de/effigenix/infrastructure/stub/StubUserRepository.java @@ -0,0 +1,71 @@ +package de.effigenix.infrastructure.stub; + +import de.effigenix.domain.usermanagement.User; +import de.effigenix.domain.usermanagement.UserId; +import de.effigenix.domain.usermanagement.UserRepository; +import de.effigenix.domain.usermanagement.UserStatus; +import de.effigenix.shared.common.RepositoryError; +import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Optional; + +@Repository +@Profile("no-db") +public class StubUserRepository implements UserRepository { + + private static final RepositoryError.DatabaseError STUB_ERROR = + new RepositoryError.DatabaseError("Stub-Modus: keine Datenbankverbindung"); + + @Override + public Result> findById(UserId id) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByUsername(String username) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByEmail(String email) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByBranchId(String branchId) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findByStatus(UserStatus status) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result> findAll() { + return Result.failure(STUB_ERROR); + } + + @Override + public Result save(User user) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result delete(User user) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result existsByUsername(String username) { + return Result.failure(STUB_ERROR); + } + + @Override + public Result existsByEmail(String email) { + return Result.failure(STUB_ERROR); + } +} diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaRoleRepository.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaRoleRepository.java index 540f775..b7c159e 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaRoleRepository.java +++ b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaRoleRepository.java @@ -7,6 +7,7 @@ import de.effigenix.domain.usermanagement.RoleName; import de.effigenix.domain.usermanagement.RoleRepository; import de.effigenix.infrastructure.usermanagement.persistence.mapper.RoleMapper; import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -27,6 +28,7 @@ import java.util.stream.Collectors; * @Transactional ensures database consistency. */ @Repository +@Profile("!no-db") @Transactional(readOnly = true) public class JpaRoleRepository implements RoleRepository { diff --git a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaUserRepository.java b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaUserRepository.java index e038dda..32cb9b6 100644 --- a/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaUserRepository.java +++ b/backend/src/main/java/de/effigenix/infrastructure/usermanagement/persistence/repository/JpaUserRepository.java @@ -7,6 +7,7 @@ import de.effigenix.domain.usermanagement.UserRepository; import de.effigenix.domain.usermanagement.UserStatus; import de.effigenix.infrastructure.usermanagement.persistence.mapper.UserMapper; import de.effigenix.shared.common.Result; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; @@ -27,6 +28,7 @@ import java.util.stream.Collectors; * @Transactional ensures database consistency. */ @Repository +@Profile("!no-db") @Transactional(readOnly = true) public class JpaUserRepository implements UserRepository { diff --git a/backend/src/main/resources/application-no-db.yml b/backend/src/main/resources/application-no-db.yml new file mode 100644 index 0000000..e167e7e --- /dev/null +++ b/backend/src/main/resources/application-no-db.yml @@ -0,0 +1,8 @@ +spring: + autoconfigure: + exclude: + - org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration + - org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration + - org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration + - org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration + - org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration