2

I'm asking for elements that have a recursive relationship in one table:

child id | parent id

1 | null

2 | 1

3 | 1

4 | 2

And so on...To ask for this whole datastructure it takes about 10 secondsAt the moment I can't redesign this table because it cost too much time (for more information: Spring Repository performance issues with recursive ORM Class)

Now, I am thinking about preloading all data during spring startup in a bean, so that the client "communicates" with the bean and I update the data in bean and database. I think it doesn't matter how long this startup takes, but it matters how long the user has to wait for its answer.

So far, I didn't manage it to preload it. I was trying to create a bean like this:

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

It doesn't work cause I get an error:

Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?

By the way, I create all my beans in AppConfig. When I delete this "allSkills" bean, it works again.

UPDATE

@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();
}
2
  • I don't see how this new bean should solve the problem. I would create a eager-initialized, session-scoped bean which caches the result Commented Feb 16, 2016 at 8:56
  • It shifts my problem more or less. The server-startup will take longer, but I can access all skills much faster, when the application is started. I even loading my children eagerly. Commented Feb 16, 2016 at 9:13

2 Answers 2

3

You can listen for the ApplicationReadyEvent, and then call the method (from @yglodt's answer) to initialise your cache.

Example code:

@Component
public class MyEventsListener {

    @Autowired
    SkillsService skillsService;

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

Also remember that if you call getAllSkills() from within the SkillsService bean itself, you will not hit the cache, because the method is only advised when it is called on an injected proxy of the class.

If you're deploying the application as an executable jar (rather than a war file), then the simplest solution is to invoke the code that you want to run at start-up from your 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();
    }

}
Sign up to request clarification or add additional context in comments.

10 Comments

Thank you for your answer. There are many ways to implement such events. I read something about @EventListener or to implement the Application, but so far I am unsure about the best way. Do you have any examples how to call my DAO function during startup?
After actually trying it out, I think you should listen for the ApplicationReadyEvent not the ApplicationStartedEvent.
Do I need to register this class anywhere? The onApplicationReady-method is not invoked for any time.
Are you using component scanning? If so, it should be picked up due to its @Component annotation (as long as its package is included in the scan scope). Otherwise, ditch @Component, and register it in a @Bean method in your config class.
If you can't get the events mechanism to work, then you can always initialise the cache by calling the service from you main method. (I've added the alternative code to my answer.)
|
2

A Cache would solve your requirement elegantly.

Ideally you would have a Service which has a method which returns the skills.

This method could looke like this:

import org.springframework.cache.annotation.Cacheable;

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

To enable Caching in Spring Boot, add the @EnableCaching annotation to your configurations class, and add a Bean to configure it:

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");
}

This way, the method getAllSkills is just executed once, the first time it's called, and afterwards the values are returned from the cache-manager without even calling the method.

2 Comments

Where do I put the "getAllSkills"-method? I can't use it in my SkillDAO-Interface. Eclipse says "The annotation @Cacheable is disallowed for this location". @Repository public interface SkillDAO extends CrudRepository<Skill, Integer>, CustomSkillDAO { @Cacheable(value = "skills", key = "#root.methodName") public List<Skill> findBySkill(Skill skill);
Ok, I imported the javax.persistence.Cacheable instead of org.springframework.cache.annotation.Cacheable. Now I can annotate the method. ... Wow, when I invoked the method onces, the next call takes just 3 seconds instead of 13 seconds. Can I tell spring to invoke method once during startup or something? Is this cache stored at clientside or serverside?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.