23

I want to implement db data init via DataSourceInitializer.

I have these as methods just below my Spring Boot main method, but it seems that it doesn't get executed at all (I tried with intentional removal of characters just to trigger an error which would confirm the execution. Nothing happened.):

@ConfigurationProperties(prefix="spring.datasource")
@Bean
public DataSource getDataSource() {

    // i was hoping this was going to pull my current datasource, as 
    // defined in application.properties
    return DataSourceBuilder
            .create()
            .build();
}


@Bean
public DataSourceInitializer dataSourceInitializer() {
    ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
    resourceDatabasePopulator.addScript(new ClassPathResource("/data/init/initData.sql"));

    DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();

    // the call to the above method
    dataSourceInitializer.setDataSource(getDataSource());


    dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);

    return dataSourceInitializer;
}

UPDATE: This question was aimed at getting a reference to the dataSource in use. This question explains how to init the data in a very simple way: DataSourceInitializer is not working on Spring boot 1.2

developer10
  • 1,390
  • 2
  • 13
  • 30

2 Answers2

43

If you have a datasource already created it will be in the spring container, so:

@Autowired
DataSource dataSource;

Should do it.

Essex Boy
  • 6,423
  • 2
  • 15
  • 19
  • Did the changes but still no changes in the db. Do you happen to know what else might be wrong with my code? – developer10 Mar 31 '17 at 14:29
  • 4
    Java magic! Thanks for this. In my case I had multiple possible autowire choices so had to specify a more granular datasource class as follows: `@Autowired private HikariDataSource dataSource;` – Shane May 25 '18 at 15:37
  • what if you have different datasource? – itro Mar 27 '19 at 08:20
  • @itro you can Autowire any interface or class that's in the Spring container, if there is a unique match it will be found. – Essex Boy Mar 27 '19 at 09:45
0

You are saying you have those method below your application main method and you are not autowiring the datasource, so you are creating an instance directly and so that not using the properties. You need to use the singleton object created by Spring. In order to do that you have two possibilities:

First option, and the one you should use, is declare a config class to create your beans:

@Configuration
public class DatasourceConfig
{

    @ConfigurationProperties(prefix="spring.datasource")
    @Bean
    public DataSource getDataSource() {

        // i was hoping this was going to pull my current datasource, as 
        // defined in application.properties
        return DataSourceBuilder
                .create()
                .build();
    }

    @Bean
    public DataSourceInitializer dataSourceInitializer() {
        ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
        resourceDatabasePopulator.addScript(new ClassPathResource("/data/init/initData.sql"));

        DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();

        // the call to the above method
        dataSourceInitializer.setDataSource(getDataSource());


        dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);

        return dataSourceInitializer;
    }

}

Using @Configuration, even calling the method directly, as Configuration classes are subclassed at startup-time with CGLIB, you are getting the object created by Spring.

Further information about how Java-based configuration works internally

Second option is auto-wire the datasource in the second method:

@Bean
@Autowired
public DataSourceInitializer dataSourceInitializer(DataSource myDatasource) {
    ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();
    resourceDatabasePopulator.addScript(new ClassPathResource("/data/init/initData.sql"));

    DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();

    dataSourceInitializer.setDataSource(myDatasource);
    dataSourceInitializer.setDatabasePopulator(resourceDatabasePopulator);

    return dataSourceInitializer;
}
alfcope
  • 2,107
  • 2
  • 11
  • 17
  • See update at the end of my question. It works that way, but sure, it's pretty much magic and I aim to learn how to move away from Boot's autoconfiguration. Now, I tried both `@Autowired`ing `DataSource` as a field, and as you suggested, by placing it below `@Bean`. Neither way worked. – developer10 Mar 31 '17 at 15:36
  • @developer10 Is your bean being initialised fine, using the properties? Is the DataSourceInitializer being created? If not, could you show your main method, please? – alfcope Mar 31 '17 at 16:19
  • My main method is a standard SpringBoot's one, just the standard line. I don't know how I can check what you're asking for - I don't see any errors that would suggest it isn't. However, I suspect it's not being called at all. Is that possible? Note: At this moment I'm fine with the default behaviour -- Spring Boot pulls and exectutes the `data.sql` file which I placed in the root of the resources (as nothing else is necessary for this to work - I just temporarily commented out the method in question). – developer10 Mar 31 '17 at 16:22
  • 1
    Just log something in your `@Bean` methods. Check also, when starting your app, the bean is not override. Look for something like `Overriding bean definition for bean 'dataSourceInitializer'` – alfcope Mar 31 '17 at 16:43