7

I want to fill some tables of my DB from a text file on startup, I want my initialization method to be called only when my application do start.

I am using Spring (+MVC) and Hibernate with MySQL.

how can I do?

Fabio B.
  • 9,138
  • 25
  • 105
  • 177
  • possible duplicate of [spring web application initialization from database on startup](http://stackoverflow.com/questions/7082594/spring-web-application-initialization-from-database-on-startup) – Tomasz Nurkiewicz Mar 13 '12 at 08:16
  • 1
    @Tomasz Nurkiewicz: is is not a duplicate of http://stackoverflow.com/questions/7082594/spring-web-application-initialization-from-database-on-startup, because the information flow is the other way arround, so one could use other ways to solve the problem. – Ralph Mar 13 '12 at 08:46
  • It's very similar. Mine is a little more generic. – Fabio B. Mar 13 '12 at 09:16
  • Possible duplicate of [Execute method on startup in spring](http://stackoverflow.com/questions/2401489/execute-method-on-startup-in-spring) – Alex K Aug 03 '16 at 07:48

6 Answers6

18

You can create an application listener, it's designed specifically for such needs. In this case it will be executed every time context is started (or refreshed).

@Component
public class DatabaseFillerOnStartup implements ApplicationListener<ContextRefreshedEvent> {
    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        ...
    }
}
sinuhepop
  • 20,010
  • 17
  • 72
  • 107
  • Exactly what I was looking for to do a self-diagnostic. Thank you! – Marquee Jul 03 '14 at 15:24
  • But this will be invoked in test cases as well and DB is not available for test cases. How to address such scenario? – Pokuri May 06 '15 at 05:49
  • @Pokuri: You have a couple of options. 1) don't declare this bean in `@Configuration` for tests, the same way you probably don't declare `@Controller` or `@Scheduled` tasks. 2) Add some conditional logic in this method, maybe analysing stack trace in order to know if you are in a jUnit test. Obviously, first is preferred. – sinuhepop May 07 '15 at 01:28
7

Hibernate comes with an way to add some files with sql statements that will be executed on startup.

The parameter is hibernate.hbm2ddl.import_files.

@See Hibernate Reference: Chapter 3.4. Optional configuration properties

  • Table 3.7. Miscellaneous Properties
  • hibernate.hbm2ddl.import_files:

Comma-separated names of the optional files containing SQL DML statements executed during the SessionFactory creation. This is useful for testing or demoing: by adding INSERT statements for example you can populate your database with a minimal set of data when it is deployed.

File order matters, the statements of a give file are executed before the statements of the following files. These statements are only executed if the schema is created ie if hibernate.hbm2ddl.auto is set to create or create-drop.

e.g. /humans.sql,/dogs.sql

I fond some hints that this may only work if hibernate is started in 'create' mode. But I am not sure.

Ralph
  • 118,862
  • 56
  • 287
  • 383
  • very useful. It's not the answer to the question itself but.... THAT'S just what I need! THANK YOU! However, I can't make import_files work :( Instead of it I put a "import.sql" in my classpath, it executes perfectly! – Fabio B. Mar 13 '12 at 09:15
5

Use the postconstuct annotation somewhere inside a bean :

@PostConstruct
public void init() {
     //startup logic here
}

Probably makes (desgin) sense to use a configuration bean, but it can be any bean at all.

NimChimpsky
  • 46,453
  • 60
  • 198
  • 311
  • Ok so the right way can be making a "Initializator" singleton and @PostConstruct a method? Thank you! :) – Fabio B. Mar 13 '12 at 08:19
  • This does not work for starting AsyncTasks. Use sinuhepop's recommendation below - http://stackoverflow.com/a/9680800/1019307 – HankCa Jan 08 '15 at 03:26
0

Two options,

First: Let hibernate create your ddl everytime the services is startup(Only for local or dev environment)

<bean id="hibernateVendor" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"
      p:showSql="false"
      p:generateDdl="true"/>

Or create in spring a Bean with init-lazy false and add in there the logic to create the database if you dont have it or inject your script, or do whatever you want.

 <bean id="bootstrapStartup" class="com.tscompany.rest.bootstrap.BootstrapStartup" lazy-init="false" init-method="init"/>
paul
  • 12,873
  • 23
  • 91
  • 153
0

You can create some bean in root-context configuration - like

<bean id="someBean" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="someProperty">
        ... description of the property
    </property>
    <property name="targetMethod" value="methodName" />
</bean>

Thus - on application startup method "methodName" will be invoked. We have DB upgrades implemented in such a way.

Andrew Gans
  • 700
  • 6
  • 9
0

If you have your hibernate.hbm2ddl.auto property set to create or create-drop then the easiest way to do that is by setting hibernate.hbm2ddl.import_files property with SQL file name that contains SQL statements to load your initial data into the database schema, e.g. initial application users.

The following is an example method that I have in my DatabaseConfig class. It sets hibernate.hbm2ddl.import_files property with SQL file name import_initial_data.sql that contains SQL Insert statements to load my initial data to the database schema.

   @Bean
   public LocalSessionFactoryBean hibernate5SessionFactoryBean(){
      LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
      localSessionFactoryBean.setDataSource((DataSource) appContext.getBean("DataSource"));
      localSessionFactoryBean.setAnnotatedClasses( AppUser.class );
      Properties properties = new Properties();
      properties.put( "hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
      properties.put("hibernate.hbm2ddl.auto","create-drop");
      properties.put("hibernate.hbm2ddl.import_files", "import_initial_data.sql");
      properties.put("hibernate.show_sql","true");
      localSessionFactoryBean.setHibernateProperties(properties);
      return localSessionFactoryBean;
   }

This is my AppUser.java in model package:

package com.beniregev.model;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import javax.persistence.*;

@Entity
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@AllArgsConstructor(access = AccessLevel.PUBLIC)
public class AppUser {
   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private int id;
   @Column(nullable = false, unique = true)
   private String userName;
   @Column(nullable = false)
   private String password;

   public AppUser(String userName, String password) {
      this.userName = userName;
      this.password = password;
   }
}

Maven dependency for lombok:

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.16.6</version>
   <scope>provided</scope>
</dependency>

The import_initial_data.sql file that I have in resources directory:

INSERT INTO appuser(username, password) VALUES ('jesus', 'christ');
INSERT INTO appuser(username, password) VALUES ('mary', 'virgin');
INSERT INTO appuser(username, password) VALUES ('josef', 'who?');
INSERT INTO appuser(username, password) VALUES ('jeremaia', 'profet');
COMMIT;

And the result of *SELECT * FROM appuser;*:

Resultset of SELECT * FROM appuser;

Binyamin Regev
  • 914
  • 5
  • 19
  • 31