Running <jdbc:initialize-database> at the beginning of context creation

I need to execute SQL script before PropertyPlaceholderConfigurer is initialized in Spring's context, as soon as application properties are stored in the database and this script should insert them. But currently placeholder is initialized earlier, which leads to errors.

Is there a way to execute <jdbc:initialize-database data-source="dataSource" ... before <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" ... in Spring?

Or is there a way to initialize placeholderConfig bean later? I tried to use depends-on, lazy-init attributes for this bean, but it didn't help. Thanks in advance.

Answers


Here is how I solved that. I created a class Initializer. This class in its constructor executes plain old sql statements (java.sql.Statement), creates table (if it doesn't exist), and inserts properties (if they are not there). The dataSource bean is passed in the context to this constructor, and placeholderConfig bean uses depends-on="initializerBean". So, properties appear in the database before they are used.


Another solution without creating any Bean: If you've got one <jdbc:initialize-database /> you can add this property depends-on="org.springframework.jdbc.datasource.init.DataSourceInitializer#0" to your bean <bean id="placeholderConfig" />

If you have more than one <jdbc:initialize-database /> adapt the #0.


This script

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc"
       xsi:schemaLocation="http://www.springframework.org/schema/jdbc">


    <jdbc:initialize-database data-source="dataSource">
        <jdbc:script location="classpath:database/drop_schema.sql" />
        <jdbc:script location="classpath:database/create_schema.sql" />
        <jdbc:script location="classpath:database/sample_data.sql"/>
    </jdbc:initialize-database>
    <!-- Other bean definitions -->
</bean>

is essentially a shortcut to

<bean class="org.springframework.jdbc.datasource.init.DataSourceInitializer" id="dataSourceInitializer">
    <property name="databasePopulator" ref="resourceDatabasePopulator"/>
    <property name="dataSource" ref="dataSource"/>
</bean>
<bean id="resourceDatabasePopulator"
      class="org.springframework.jdbc.datasource.init.ResourceDatabasePopulator">
    <property name="scripts">
        <array>
            <value>classpath:database/drop_schema.sql</value>
            <value>classpath:database/create_schema.sql</value>
            <value>classpath:database/sample_data.sql</value>
        </array>
    </property>
</bean>

Note that I've added id to the DataSourceInitializer bean. Now you can reference it in PropertyPlaceholderConfigurer's depends-on attribute. That way you declare that your PropertyPlaceholderConfigurer should be created after DataSourceInitializer.

<bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" depends-on="dataSourceInitializer"/>

In my case db was initialized after the LocalContainerEntityManagerFactoryBean had been created and Hibernate was unable to validate my schema.


The documentation explains how to deal with these problems: http://static.springsource.org/spring/docs/3.0.0.RC3/reference/html/ch12s09.html

12.9.1.1 Initialization of Other Components that Depend on the Database

Our use case was arguably even more complex. We're using flyway for database migrations and it needs to be started before the entityManagerFactory is created. The problem for us was that <jdbc:initialize-database /> was only used in our migration tests and thus initialized in a different application context than flyway and the entityManagerFactory. So we couldn't simply use L. BIZE answer and let our flyway bean depend on org.springframework.jdbc.datasource.init.DataSourceInitializer#0 because it might not exist (i.e. in production it doesn't exist). We ended up creating a custom factory bean like this:

class OptionalBeanInitializer extends AbstractFactoryBean implements BeanFactoryAware {
    private String beanName;
    private BeanFactory beanFactory;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    @Override
    public Class<?> getObjectType() {
        return OptionalBeanInitializer.class;
    }

    public void setBeanName(String beanName) {
        this.beanName = beanName;
    }

    @Override
    protected Object createInstance() throws Exception {
        if (beanFactory.containsBean(beanName)) {
            // Initialize
            beanFactory.getBean(beanName);
        }
        return new OptionalBeanInitializer();
    }
}

Which we could use to depend on our optional dependency like this:

<bean id="optionalDataSourceInitializer" class="com.x.y.z.OptionalBeanInitializer">
       <property name="beanName" value="org.springframework.jdbc.datasource.init.DataSourceInitializer#0"/>
</bean>   

 <bean id="flyway" class="com.googlecode.flyway.core.Flyway" init-method="migrate" depends-on="optionalDataSourceInitializer">
       <property name="dataSource" ref="dataSource"/>
 </bean>

 <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory"
          depends-on="flyway, optionalDataSourceInitializer">
       <property name="dataSource" ref="dataSource"/>
 </bean>

The OptionalBeanInitializer will take care of initializing the org.springframework.jdbc.datasource.init.DataSourceInitializer#0 bean only if it exists.


There are two simpler ways to add the depend-on attribute only for test context, and not for production context.

1/Override the bean in the test context. In our case here is the production context :

<bean id="placeholderConfig" />

And the unit test context :

<bean id="placeholderConfig" depends-on="org.springframework.jdbc.datasource.init.DataSourceInitializer#0" />

Make sure that the unit test context is loaded before the production context and the good placeholderConfig bean will be instanciated, after the jdbc:initialize-database phase.

2/Another way is to use profiles

<beans profile="!test">
    <bean id="placeholderConfig" .../>
</beans>
<beans profile="test">
    <jdbc:initialize-database data-source="dataSource" .../>
    <bean id="placeholderConfig" depends-on="org.springframework.jdbc.datasource.init.DataSourceInitializer#0" .../>
</beans>

Need Your Help

Passing muliline and single line parameters to a MS DOS bat file

vb.net batch-file cmd dos gnokii

im try to send sms using gnokii sms library (http://gnokii.org/) with vb .net,im created a seperate bat file and call that bat file from my vb.net code