【问题标题】:Spring Boot with session-based data source具有基于会话的数据源的 Spring Boot
【发布时间】:2015-04-21 00:46:48
【问题描述】:

对于 Web 应用程序来说应该是一个非常常见的用例,我一直在努力解决问题。我有一个使用 REST 存储库、JPA 等的 Spring-Boot 应用程序。问题是我有两个数据源:

  • 包含用户身份验证信息的嵌入式 H2 数据源
  • 特定于经过身份验证的用户的实际数据的 MySQL 数据源

由于第二个数据源特定于经过身份验证的用户,因此我正在尝试使用 AbstractRoutingDataSource 在身份验证后根据 Principal 用户路由到正确的数据源。

让我抓狂的是,Spring-Boot 在启动时与我作斗争,以实例化这个数据源。我已经尝试了所有我能想到的方法,包括 Lazy 和 Scope 注释。如果我使用 Session 范围,应用程序会在启动时抛出关于没有会话存在的错误。 @Lazy 似乎根本没有帮助。无论我使用什么注释,Spring Boot 在启动时都会对数据库进行实例化,并且找不到任何实质上导致整个应用程序崩溃的查找键。

另一个问题是 Rest Repository API 让 IMO 有一种糟糕的方式来指定要使用的实际数据源。如果您有多个使用 Spring Boot 的数据源,则必须处理 Qualifier 注解,这是运行时调试的噩梦。

非常感谢任何建议。

【问题讨论】:

  • 这里似乎发生的是,第二个 Spring-Boot 初始化嵌入式数据源(不是惰性的),它扫描并找到另一个数据源。 EntityManager 会感到困惑,因为在扫描期间发现了多个数据源,即使它们具有不同的限定符,它也不知道这一点,并引发有关多个可用源的错误。但是,如果我使用 Primary 注释来指定,那也会覆盖任何 @Lazy init 和 Spring-Boot 尝试立即初始化 Primary。
  • 我不认为 @Primary@Lazy 不兼容或任何东西。但是我也不确定您要做什么。也许如果您使用 2 DataSources 和 JPA 创建一个小项目,并在此处粘贴链接,那么有人可以尝试了解您的需求。
  • 我将制定一个项目。但是,我需要的是一个带有两个数据库的 Spring-Boot 项目。一个连接立即可用。其他“路由”数据源仅应在用户经过身份验证后实例化并与会话范围一起使用。我应该注意到,这在常规 Spring 上工作得非常好,但对 Spring-Boot 来说完全是无聊的,因为它非常倾向于自动配置一切。
  • 如果它在非引导应用程序中工作,我保证它可以在引导应用程序中工作,一旦你弄清楚如何关闭默认行为。

标签: java spring spring-boot spring-data-rest


【解决方案1】:

您的问题在于身份验证管理器配置。所有示例和指南都将其设置在 GlobalAuthenticationConfigurerAdapter 中,例如它看起来像你的 SimpleEmbeddedSecurityConfiguration 的内部类:

@Configuration
public static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter
{
    @Bean(name = Global.AUTHENTICATION_DATA_QUALIFIER + "DataSource")
    public DataSource dataSource()
    {
        return new EmbeddedDatabaseBuilder().setName("authdb").setType(EmbeddedDatabaseType.H2).addScripts("security/schema.sql", "security/data.sql").build();
    }

    @Override
    public void init(AuthenticationManagerBuilder auth) throws Exception
    {
            auth.jdbcAuthentication().dataSource(dataSource()).passwordEncoder(passwordEncoder());
    }
}

如果你不使用GlobalAuthenticationConfigurerAdapter,那么DataSource 会在创建安全过滤器期间被Spring Data REST 拾取(在@Primary DataSource bean 甚至被注册之前)和整个JPA初始化开始超早(坏主意)。

更新:身份验证管理器不是唯一的问题。如果你需要一个会话范围的@PrimaryDataSource(我想说这很不寻常),你需要关闭所有想要在启动时访问数据库的东西(Hibernate 和 Spring Boot 在不同的地方)。示例:

spring.datasource.initialize: false
spring.jpa.hibernate.ddlAuto: none
spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults: false
spring.jpa.properties.hibernate.dialect: H2

进一步更新:如果您使用的是执行器,它还希望在启动时使用主数据源作为运行状况指示器。您可以通过提供相同类型的 bean 来覆盖它,例如

@Bean
@Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
@Lazy
public DataSourcePublicMetrics dataSourcePublicMetrics() {
    return new DataSourcePublicMetrics();
}

附:我相信 GlobalAuthenticationConfigurerAdapter 在 Spring Boot 1.2.2 中可能不是必需的,但它在 1.2.1 或 1.1.10 中。

【讨论】:

  • 谢谢,我会检查一下。但是,更根本的是,如果您有不同的数据源并且其中一个恰好不需要用于身份验证,这将如何工作?如果我真的有多个 REST 所需的不同数据源怎么办?另外,我不确定这个解决方案是否能解决我的“早期初始化”Spring-Boot 问题,它忽略了 REST 服务上的 @Lazy,但我一定会试一试。
  • 您是否真的对这个项目进行了更改并运行它?据我所知,它根本没有解决问题。问题基本上是我无法让 Spring-Boot 在启动时不实例化 UserSpecificDataSource。这实际上应该是会话范围的配置,但如果您设置此范围,Spring-Boot 会失败,因为它无论如何都会尝试实例化。事实上,我似乎无法让 Spring-Boot 接受任何带有会话范围的 @Configuration 。它在启动时触发,然后失败。
  • @Configuration 与会话范围没有意义(至少对我来说),所以我要么将它们转换为 @Component 要么删除它们。如果您愿意,我可以将您的代码(经过大量修改,因为它不是真正的“最小”)发布回 github。 (但会话范围的事情与 JPA 问题无关。)
  • 实际上我明白你的意思(一旦我清理了会话范围的 bean 一点)。你的 @Primary DataSource 是会话范围的,Spring Boot 想在启动时使用它(只是为了检查它是否有效,但当然它没有)。我会看看关闭它的最佳方法是什么。
  • 是的,查看堆栈跟踪,它建立一个connection() 只是为了检查然后失败。仅供参考,我什至尝试在此数据源上使用 LazyConnectionDataSourceProxy 包装器,但也失败了。无论我做什么,Spring-Boot 都会在启动时不断尝试连接到实际的数据源。直接使用 Spring 和基于会话的 AbstractRoutingDataSource 实现没有这样的问题。
猜你喜欢
  • 2016-08-27
  • 2014-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-16
  • 2017-04-06
  • 2020-06-21
相关资源
最近更新 更多