【问题标题】:Spring boot preloading data from database in beanSpring Boot从bean中的数据库预加载数据
【发布时间】:2016-02-16 08:46:33
【问题描述】:

我要求在一个表中具有递归关系的元素:

孩子ID |父 ID

1 |空

2 | 1

3 | 1

4 | 2

等等...查询整个数据结构大约需要 10 秒目前我无法重新设计此表,因为它花费了太多时间(更多信息:Spring Repository performance issues with recursive ORM Class

现在,我正在考虑在 bean 中在 spring 启动期间预加载所有数据,以便客户端与 bean“通信”,并更新 bean 和数据库中的数据。我认为这次启动需要多长时间并不重要,重要的是用户必须等待多长时间。

到目前为止,我还没有设法预加载它。我试图创建一个这样的 bean:

public class AppConfig extends WebMvcConfigurerAdapter {
...
@Autowired
SkillDAO skillDAO
...
@Bean(name="allSkills")
public List<Skill> allSkills(){
    return skillDAO.findBySkill(null);
}
...

它不起作用,因为我收到一个错误:

原因:org.springframework.beans.factory.BeanCurrentlyInCreationException:创建名为“dataSource”的 bean 时出错:当前正在创建请求的 bean:是否存在无法解析的循环引用?

顺便说一下,我在 AppConfig 中创建了所有的 bean。当我删除这个“allSkills”bean 时,它又可以工作了。

更新

@Component
public class MyListener {

@Autowired
private SkillDAO skillDAO;

@EventListener
public void onApplicationReady(ApplicationReadyEvent ready) {
    System.out.println("++++++++++++++ HALLO +++++++++++++");
    skillDAO.findBySkill(null);
}
}

...

@Bean(name="skillBean")
public MyListener myListener() {
    return new MyListener();
}

【问题讨论】:

  • 我不明白这个新 bean 应该如何解决问题。我会创建一个预先初始化的、会话范围的 bean 来缓存结果
  • 它或多或少地改变了我的问题。服务器启动将花费更长的时间,但是当应用程序启动时,我可以更快地访问所有技能。我什至热切地加载我的孩子。

标签: java spring datasource javabeans


【解决方案1】:

您可以监听ApplicationReadyEvent,然后调用该方法(来自@yglodt 的answer)来初始化您的缓存。

示例代码:

@Component
public class MyEventsListener {

    @Autowired
    SkillsService skillsService;

    @EventListener
    public void onApplicationReady(ApplicationReadyEvent ready) {
        skillsService.getAllSkills();
    }
}

还请记住,如果您从 SkillsService bean 本身调用 getAllSkills(),则不会命中缓存,因为该方法仅在类的注入 proxy 上调用时才会被建议。

如果您将应用程序部署为可执行 jar(而不是 war 文件),那么最简单的解决方案是从您的 main method 调用您希望在启动时运行的代码:

public class Application {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(Application.class, args);
        SkillsService skillsService = context.getBean(SkillsService.class);
        skillsService.getAllSkills();
    }

}

【讨论】:

  • 感谢您的回答。有很多方法可以实现此类事件。我读了一些关于@EventListener 或实现应用程序的东西,但到目前为止我不确定最好的方法。您有任何示例如何在启动期间调用我的 DAO 函数吗?
  • 在实际尝试之后,我认为您应该听ApplicationReadyEvent 而不是ApplicationStartedEvent
  • 我需要在任何地方注册这个课程吗?任何时候都不会调用 onApplicationReady 方法。
  • 你在使用component scanning吗?如果是这样,由于它的@Component 注释(只要它的包包含在扫描范围内),它应该被拾取。否则,放弃@Component,并将其注册到配置类的@Bean 方法中。
  • 如果你不能让事件机制工作,那么你总是可以通过从你的主方法调用服务来初始化缓存。 (我已将替代代码添加到我的答案中。)
【解决方案2】:

Cache 将优雅地解决您的要求。

理想情况下,您应该有一个 Service,它有一个返回技能的方法。

此方法可能如下所示:

import org.springframework.cache.annotation.Cacheable;

@Cacheable(value = "skills", key = "#root.methodName")
public List<Skill> getAllSkills() {
    return ... your skills ...;
}

要在 Spring Boot 中启用缓存,请将 @EnableCaching 注解添加到您的配置类中,并添加 Bean 进行配置:

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;

@Bean
public CacheManager cacheManager() {
    return new ConcurrentMapCacheManager("skills", "othercachename");
}

这样,getAllSkills 方法只执行一次,第一次被调用,然后从缓存管理器返回值,甚至不调用该方法。

【讨论】:

  • 我应该把“getAllSkills”方法放在哪里?我不能在我的 SkillDAO 界面中使用它。 Eclipse 说“此位置不允许使用注释 @Cacheable”。 @Repository public interface SkillDAO extends CrudRepository&lt;Skill, Integer&gt;, CustomSkillDAO { @Cacheable(value = "skills", key = "#root.methodName") public List&lt;Skill&gt; findBySkill(Skill skill);
  • 好的,我导入了 javax.persistence.Cacheable 而不是 org.springframework.cache.annotation.Cacheable。现在我可以注释该方法。 ...哇,当我调用该方法一次时,下一次调用只需 3 秒而不是 13 秒。我可以告诉spring在启动期间调用一次方法吗?这个缓存是存储在客户端还是服务器端?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-21
  • 2017-04-05
  • 1970-01-01
  • 2014-12-10
  • 2019-08-11
  • 2016-10-28
相关资源
最近更新 更多